2020-02-12 20:06:46 +01:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
#include "TermControl.h"
|
|
|
|
#include <argb.h>
|
|
|
|
#include <DefaultSettings.h>
|
|
|
|
#include <unicode.hpp>
|
|
|
|
#include <Utf16Parser.hpp>
|
|
|
|
#include <Utils.h>
|
|
|
|
#include <WinUser.h>
|
|
|
|
#include <LibraryResources.h>
|
|
|
|
#include "..\..\types\inc\GlyphWidth.hpp"
|
|
|
|
|
|
|
|
#include "TermControl.g.cpp"
|
|
|
|
#include "TermControlAutomationPeer.h"
|
|
|
|
|
|
|
|
using namespace ::Microsoft::Console::Types;
|
2020-08-08 01:21:09 +02:00
|
|
|
using namespace ::Microsoft::Console::VirtualTerminal;
|
2020-02-12 20:06:46 +01:00
|
|
|
using namespace ::Microsoft::Terminal::Core;
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
using namespace winrt::Windows::Graphics::Display;
|
2020-02-12 20:06:46 +01:00
|
|
|
using namespace winrt::Windows::UI::Xaml;
|
2020-02-27 01:35:16 +01:00
|
|
|
using namespace winrt::Windows::UI::Xaml::Input;
|
2020-02-12 20:06:46 +01:00
|
|
|
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
|
|
|
|
using namespace winrt::Windows::UI::Core;
|
2020-03-10 18:55:57 +01:00
|
|
|
using namespace winrt::Windows::UI::ViewManagement;
|
2020-03-13 01:44:28 +01:00
|
|
|
using namespace winrt::Windows::UI::Input;
|
2020-02-12 20:06:46 +01:00
|
|
|
using namespace winrt::Windows::System;
|
|
|
|
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
|
|
|
|
2020-06-12 21:51:37 +02:00
|
|
|
// The minimum delay between updates to the scroll bar's values.
|
|
|
|
// The updates are throttled to limit power usage.
|
|
|
|
constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8);
|
|
|
|
|
2020-06-23 23:05:40 +02:00
|
|
|
// The minimum delay between updating the TSF input control.
|
|
|
|
constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(100);
|
|
|
|
|
2020-10-28 21:24:43 +01:00
|
|
|
// The minimum delay between updating the locations of regex patterns
|
|
|
|
constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(500);
|
|
|
|
|
Add copyFormatting keybinding arg and array support (#6004)
Adds array support for the existing `copyFormatting` global setting.
This allows users to define which formats they would specifically like
to be copied.
A boolean value is still accepted and is translated to the following:
- `false` --> `"none"` or `[]`
- `true` --> `"all"` or `["html", "rtf"]`
This also adds `copyFormatting` as a keybinding arg for `copy`. As with
the global setting, a boolean value and array value is accepted.
CopyFormat is a WinRT enum where each accepted format is a flag.
Currently accepted formats include `html`, and `rtf`. A boolean value is
accepted and converted. `true` is a conjunction of all the formats.
`false` only includes plain text.
For the global setting, `null` is not accepted. We already have a
default value from before so no worries there.
For the keybinding arg, `null` (the default value) means that we just do
what the global arg says to do. Overall, the `copyFormatting` keybinding
arg is an override of the global setting **when using that keybinding**.
References #5212 - Spec for formatted copying
References #2690 - disable html copy
Validated behavior with every combination of values below:
- `copyFormatting` global: { `true`, `false`, `[]`, `["html"]` }
- `copyFormatting` copy arg:
{ `null`, `true`, `false`, `[]`, `[, "html"]`}
Closes #4191
Closes #5262
2020-08-15 03:02:24 +02:00
|
|
|
DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::TerminalControl::CopyFormat);
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|
|
|
{
|
|
|
|
// Helper static function to ensure that all ambiguous-width glyphs are reported as narrow.
|
|
|
|
// See microsoft/terminal#2066 for more info.
|
|
|
|
static bool _IsGlyphWideForceNarrowFallback(const std::wstring_view /* glyph */)
|
|
|
|
{
|
|
|
|
return false; // glyph is not wide.
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool _EnsureStaticInitialization()
|
|
|
|
{
|
|
|
|
// use C++11 magic statics to make sure we only do this once.
|
|
|
|
static bool initialized = []() {
|
|
|
|
// *** THIS IS A SINGLETON ***
|
|
|
|
SetGlyphWidthFallback(_IsGlyphWideForceNarrowFallback);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}();
|
|
|
|
return initialized;
|
|
|
|
}
|
|
|
|
|
2020-08-07 16:46:52 +02:00
|
|
|
TermControl::TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection) :
|
2020-02-12 20:06:46 +01:00
|
|
|
_connection{ connection },
|
|
|
|
_initializedTerminal{ false },
|
|
|
|
_settings{ settings },
|
|
|
|
_closing{ false },
|
2020-06-12 21:51:37 +02:00
|
|
|
_isInternalScrollBarUpdate{ false },
|
2020-02-12 20:06:46 +01:00
|
|
|
_autoScrollVelocity{ 0 },
|
|
|
|
_autoScrollingPointerPoint{ std::nullopt },
|
|
|
|
_autoScrollTimer{},
|
|
|
|
_lastAutoScrollUpdateTime{ std::nullopt },
|
2020-05-20 22:17:17 +02:00
|
|
|
_desiredFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8 },
|
|
|
|
_actualFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false },
|
2020-02-12 20:06:46 +01:00
|
|
|
_touchAnchor{ std::nullopt },
|
|
|
|
_cursorTimer{},
|
Add support for the "blink" graphic rendition attribute (#7490)
This PR adds support for the _blink_ graphic rendition attribute. When a
character is output with this attribute set, it "blinks" at a regular
interval, by cycling its color between the normal rendition and a dimmer
shade of that color.
The majority of the blinking mechanism is encapsulated in a new
`BlinkingState` class, which is shared between the Terminal and Conhost
implementations. This class keeps track of the position in the blinking
cycle, which determines whether characters are rendered as normal or
faint.
In Windows Terminal, the state is stored in the `Terminal` class, and in
Conhost it's stored in the `CONSOLE_INFORMATION` class. In both cases,
the `IsBlinkingFaint` method is used to determine the current blinking
rendition, and that is passed on as a parameter to the
`TextAttribute::CalculateRgbColors` method when these classes are
looking up attribute colors.
Prior to calculating the colors, the current attribute is also passed to
the `RecordBlinkingUsage` method, which keeps track of whether there are
actually any blink attributes in use. This is used to determine whether
the screen needs to be refreshed when the blinking cycle toggles between
the normal and faint renditions.
The refresh itself is handled by the `ToggleBlinkingRendition` method,
which is triggered by a timer. In Conhost this is just piggybacking on
the existing cursor blink timer, but in Windows Terminal it needs to
have its own separate timer, since the cursor timer is reset whenever a
key is pressed, which is not something we want for attribute blinking.
Although the `ToggleBlinkingRendition` is called at the same rate as the
cursor blinking, we actually only want the cells to blink at half that
frequency. We thus have a counter that cycles through four phases, and
blinking is rendered as faint for two of those four. Then every two
cycles - when the state changes - a redraw is triggered, but only if
there are actually blinking attributes in use (as previously recorded).
As mentioned earlier, the blinking frequency is based on the cursor
blink rate, so that means it'll automatically be disabled if a user has
set their cursor blink rate to none. It can also be disabled by turning
off the _Show animations in Windows_ option. In Conhost these settings
take effect immediately, but in Windows Terminal they only apply when a
new tab is opened.
This PR also adds partial support for the `SGR 6` _rapid blink_
attribute. This is not used by DEC terminals, but was defined in the
ECMA/ANSI standards. It's not widely supported, but many terminals just
it implement it as an alias for the regular `SGR 5` blink attribute, so
that's what I've done here too.
## Validation Steps Performed
I've checked the _Graphic rendition test pattern_ in Vttest, and
compared our representation of the blink attribute to that of an actual
DEC VT220 terminal as seen on [YouTube]. With the right color scheme
it's a reasonably close match.
[YouTube]: https://www.youtube.com/watch?v=03Pz5AmxbE4&t=1m55s
Closes #7388
2020-09-22 01:21:33 +02:00
|
|
|
_blinkTimer{},
|
2020-03-10 15:59:16 +01:00
|
|
|
_lastMouseClickTimestamp{},
|
2020-02-12 20:06:46 +01:00
|
|
|
_lastMouseClickPos{},
|
2020-03-25 22:09:49 +01:00
|
|
|
_selectionNeedsToBeCopied{ false },
|
|
|
|
_searchBox{ nullptr }
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
_EnsureStaticInitialization();
|
2020-02-27 01:35:16 +01:00
|
|
|
InitializeComponent();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
|
|
|
|
|
2020-10-01 03:00:06 +02:00
|
|
|
auto pfnWarningBell = std::bind(&TermControl::_TerminalWarningBell, this);
|
|
|
|
_terminal->SetWarningBellCallback(pfnWarningBell);
|
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1);
|
|
|
|
_terminal->SetTitleChangedCallback(pfnTitleChanged);
|
|
|
|
|
2020-08-08 01:07:42 +02:00
|
|
|
auto pfnTabColorChanged = std::bind(&TermControl::_TerminalTabColorChanged, this, std::placeholders::_1);
|
|
|
|
_terminal->SetTabColorChangedCallback(pfnTabColorChanged);
|
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
auto pfnBackgroundColorChanged = std::bind(&TermControl::_BackgroundColorChanged, this, std::placeholders::_1);
|
|
|
|
_terminal->SetBackgroundCallback(pfnBackgroundColorChanged);
|
|
|
|
|
|
|
|
auto pfnScrollPositionChanged = std::bind(&TermControl::_TerminalScrollPositionChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
|
|
|
_terminal->SetScrollPositionChangedCallback(pfnScrollPositionChanged);
|
|
|
|
|
2020-03-31 01:21:47 +02:00
|
|
|
auto pfnTerminalCursorPositionChanged = std::bind(&TermControl::_TerminalCursorPositionChanged, this);
|
|
|
|
_terminal->SetCursorPositionChangedCallback(pfnTerminalCursorPositionChanged);
|
|
|
|
|
2020-06-30 03:55:40 +02:00
|
|
|
auto pfnCopyToClipboard = std::bind(&TermControl::_CopyToClipboard, this, std::placeholders::_1);
|
|
|
|
_terminal->SetCopyToClipboardCallback(pfnCopyToClipboard);
|
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// This event is explicitly revoked in the destructor: does not need weak_ref
|
|
|
|
auto onReceiveOutputFn = [this](const hstring str) {
|
|
|
|
_terminal->Write(str);
|
2020-10-28 21:24:43 +01:00
|
|
|
_updatePatternLocations->Run();
|
2020-03-27 00:25:11 +01:00
|
|
|
};
|
|
|
|
_connectionOutputEventToken = _connection.TerminalOutput(onReceiveOutputFn);
|
|
|
|
|
2020-08-12 15:46:53 +02:00
|
|
|
_terminal->SetWriteInputCallback([this](std::wstring& wstr) {
|
|
|
|
_SendInputToConnection(wstr);
|
|
|
|
});
|
2020-03-27 00:25:11 +01:00
|
|
|
|
2020-06-09 23:47:13 +02:00
|
|
|
_terminal->UpdateSettings(settings);
|
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Subscribe to the connection's disconnected event and call our connection closed handlers.
|
|
|
|
_connectionStateChangedRevoker = _connection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
|
|
|
|
_ConnectionStateChangedHandlers(*this, nullptr);
|
|
|
|
});
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Initialize the terminal only once the swapchainpanel is loaded - that
|
|
|
|
// way, we'll be able to query the real pixel size it got on layout
|
2020-02-27 01:35:16 +01:00
|
|
|
_layoutUpdatedRevoker = SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
|
2020-02-12 20:06:46 +01:00
|
|
|
// This event fires every time the layout changes, but it is always the last one to fire
|
|
|
|
// in any layout change chain. That gives us great flexibility in finding the right point
|
|
|
|
// at which to initialize our renderer (and our terminal).
|
|
|
|
// Any earlier than the last layout update and we may not know the terminal's starting size.
|
|
|
|
|
|
|
|
if (_InitializeTerminal())
|
|
|
|
{
|
|
|
|
// Only let this succeed once.
|
|
|
|
_layoutUpdatedRevoker.revoke();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-06-23 23:05:40 +02:00
|
|
|
_tsfTryRedrawCanvas = std::make_shared<ThrottledFunc<>>(
|
|
|
|
[weakThis = get_weak()]() {
|
|
|
|
if (auto control{ weakThis.get() })
|
|
|
|
{
|
|
|
|
control->TSFInputControl().TryRedrawCanvas();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
TsfRedrawInterval,
|
|
|
|
Dispatcher());
|
|
|
|
|
2020-10-28 21:24:43 +01:00
|
|
|
_updatePatternLocations = std::make_shared<ThrottledFunc<>>(
|
|
|
|
[weakThis = get_weak()]() {
|
|
|
|
if (auto control{ weakThis.get() })
|
|
|
|
{
|
|
|
|
control->UpdatePatternLocations();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
UpdatePatternLocationsInterval,
|
|
|
|
Dispatcher());
|
|
|
|
|
2020-06-12 21:51:37 +02:00
|
|
|
_updateScrollBar = std::make_shared<ThrottledFunc<ScrollBarUpdate>>(
|
|
|
|
[weakThis = get_weak()](const auto& update) {
|
|
|
|
if (auto control{ weakThis.get() })
|
|
|
|
{
|
2020-06-23 23:05:40 +02:00
|
|
|
control->_isInternalScrollBarUpdate = true;
|
|
|
|
|
|
|
|
auto scrollBar = control->ScrollBar();
|
|
|
|
if (update.newValue.has_value())
|
|
|
|
{
|
|
|
|
scrollBar.Value(update.newValue.value());
|
|
|
|
}
|
|
|
|
scrollBar.Maximum(update.newMaximum);
|
|
|
|
scrollBar.Minimum(update.newMinimum);
|
|
|
|
scrollBar.ViewportSize(update.newViewportSize);
|
2020-08-25 00:54:02 +02:00
|
|
|
scrollBar.LargeChange(std::max(update.newViewportSize - 1, 0.)); // scroll one "screenful" at a time when the scroll bar is clicked
|
2020-06-23 23:05:40 +02:00
|
|
|
|
|
|
|
control->_isInternalScrollBarUpdate = false;
|
2020-06-12 21:51:37 +02:00
|
|
|
}
|
|
|
|
},
|
2020-06-23 23:05:40 +02:00
|
|
|
ScrollBarUpdateInterval,
|
|
|
|
Dispatcher());
|
2020-06-12 21:51:37 +02:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast<int>(1.0 / 30.0 * 1000000));
|
|
|
|
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
|
|
|
|
_autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll });
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
_ApplyUISettings();
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
2020-02-27 01:35:16 +01:00
|
|
|
// - Loads the search box from the xaml UI and focuses it.
|
2020-02-12 20:06:46 +01:00
|
|
|
void TermControl::CreateSearchBoxControl()
|
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
// Lazy load the search box control.
|
|
|
|
if (auto loadedSearchBox{ FindName(L"SearchBox") })
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
if (auto searchBox{ loadedSearchBox.try_as<::winrt::Microsoft::Terminal::TerminalControl::SearchBoxControl>() })
|
|
|
|
{
|
|
|
|
// get at its private implementation
|
|
|
|
_searchBox.copy_from(winrt::get_self<implementation::SearchBoxControl>(searchBox));
|
|
|
|
_searchBox->Visibility(Visibility::Visible);
|
|
|
|
_searchBox->SetFocusOnTextbox();
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Search text in text buffer. This is triggered if the user click
|
|
|
|
// search button or press enter.
|
|
|
|
// Arguments:
|
|
|
|
// - text: the text to search
|
|
|
|
// - goForward: boolean that represents if the current search direction is forward
|
|
|
|
// - caseSensitive: boolean that represents if the current search is case sensitive
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void TermControl::_Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive)
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (text.size() == 0 || _closing)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Search::Direction direction = goForward ?
|
|
|
|
Search::Direction::Forward :
|
|
|
|
Search::Direction::Backward;
|
|
|
|
|
|
|
|
const Search::Sensitivity sensitivity = caseSensitive ?
|
|
|
|
Search::Sensitivity::CaseSensitive :
|
|
|
|
Search::Sensitivity::CaseInsensitive;
|
|
|
|
|
|
|
|
Search search(*GetUiaData(), text.c_str(), direction, sensitivity);
|
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
if (search.FindNext())
|
|
|
|
{
|
2020-02-28 01:42:26 +01:00
|
|
|
_terminal->SetBlockSelection(false);
|
2020-02-12 20:06:46 +01:00
|
|
|
search.Select();
|
|
|
|
_renderer->TriggerSelection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - The handler for the close button or pressing "Esc" when focusing on the
|
|
|
|
// search dialog.
|
|
|
|
// Arguments:
|
|
|
|
// - IInspectable: not used
|
|
|
|
// - RoutedEventArgs: not used
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/, RoutedEventArgs const& /*args*/)
|
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
_searchBox->Visibility(Visibility::Collapsed);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// Set focus back to terminal control
|
|
|
|
this->Focus(FocusState::Programmatic);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Given new settings for this profile, applies the settings to the current terminal.
|
|
|
|
// Arguments:
|
|
|
|
// - newSettings: New settings values for the profile in this terminal.
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
2020-08-07 16:46:52 +02:00
|
|
|
winrt::fire_and_forget TermControl::UpdateSettings(IControlSettings newSettings)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
_settings = newSettings;
|
|
|
|
auto weakThis{ get_weak() };
|
|
|
|
|
|
|
|
// Dispatch a call to the UI thread to apply the new settings to the
|
|
|
|
// terminal.
|
2020-02-27 01:35:16 +01:00
|
|
|
co_await winrt::resume_foreground(Dispatcher());
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// If 'weakThis' is locked, then we can safely work with 'this'
|
|
|
|
if (auto control{ weakThis.get() })
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_closing)
|
|
|
|
{
|
2020-06-12 21:51:37 +02:00
|
|
|
co_return;
|
2020-03-27 00:25:11 +01:00
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Update our control settings
|
|
|
|
_ApplyUISettings();
|
|
|
|
|
|
|
|
// Update the terminal core with its new Core settings
|
|
|
|
_terminal->UpdateSettings(_settings);
|
|
|
|
|
2020-04-30 02:05:29 +02:00
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
|
2020-06-19 23:09:37 +02:00
|
|
|
// Update DxEngine settings under the lock
|
|
|
|
_renderEngine->SetSelectionBackground(_settings.SelectionBackground());
|
|
|
|
|
|
|
|
_renderEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect());
|
|
|
|
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
|
|
|
|
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
|
|
|
|
|
|
|
|
switch (_settings.AntialiasingMode())
|
|
|
|
{
|
|
|
|
case TextAntialiasingMode::Cleartype:
|
|
|
|
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
|
|
|
|
break;
|
|
|
|
case TextAntialiasingMode::Aliased:
|
|
|
|
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
|
|
|
|
break;
|
|
|
|
case TextAntialiasingMode::Grayscale:
|
|
|
|
default:
|
|
|
|
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Refresh our font with the renderer
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
const auto actualFontOldSize = _actualFont.GetSize();
|
2020-02-12 20:06:46 +01:00
|
|
|
_UpdateFont();
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
const auto actualFontNewSize = _actualFont.GetSize();
|
|
|
|
if (actualFontNewSize != actualFontOldSize)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-04-30 02:05:29 +02:00
|
|
|
_RefreshSizeUnderLock();
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-12 15:46:53 +02:00
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Writes the given sequence as input to the active terminal connection,
|
|
|
|
// Arguments:
|
|
|
|
// - wstr: the string of characters to write to the terminal connection.
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void TermControl::SendInput(const winrt::hstring& wstr)
|
|
|
|
{
|
|
|
|
_SendInputToConnection(wstr);
|
|
|
|
}
|
|
|
|
|
2020-07-02 01:17:43 +02:00
|
|
|
void TermControl::ToggleRetroEffect()
|
|
|
|
{
|
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
_renderEngine->SetRetroTerminalEffects(!_renderEngine->GetRetroTerminalEffects());
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Style our UI elements based on the values in our _settings, and set up
|
|
|
|
// other control-specific settings. This method will be called whenever
|
|
|
|
// the settings are reloaded.
|
|
|
|
// * Calls _InitializeBackgroundBrush to set up the Xaml brush responsible
|
|
|
|
// for the control's background
|
|
|
|
// * Calls _BackgroundColorChanged to style the background of the control
|
|
|
|
// - Core settings will be passed to the terminal in _InitializeTerminal
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void TermControl::_ApplyUISettings()
|
|
|
|
{
|
|
|
|
_InitializeBackgroundBrush();
|
|
|
|
|
2020-05-16 00:43:00 +02:00
|
|
|
COLORREF bg = _settings.DefaultBackground();
|
2020-02-12 20:06:46 +01:00
|
|
|
_BackgroundColorChanged(bg);
|
|
|
|
|
|
|
|
// Apply padding as swapChainPanel's margin
|
|
|
|
auto newMargin = _ParseThicknessFromPadding(_settings.Padding());
|
2020-02-27 01:35:16 +01:00
|
|
|
SwapChainPanel().Margin(newMargin);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// Initialize our font information.
|
|
|
|
const auto fontFace = _settings.FontFace();
|
2020-02-26 02:10:07 +01:00
|
|
|
const short fontHeight = gsl::narrow_cast<short>(_settings.FontSize());
|
2020-05-20 22:17:17 +02:00
|
|
|
const auto fontWeight = _settings.FontWeight();
|
2020-02-12 20:06:46 +01:00
|
|
|
// The font width doesn't terribly matter, we'll only be using the
|
|
|
|
// height to look it up
|
|
|
|
// The other params here also largely don't matter.
|
|
|
|
// The family is only used to determine if the font is truetype or
|
|
|
|
// not, but DX doesn't use that info at all.
|
|
|
|
// The Codepage is additionally not actually used by the DX engine at all.
|
2020-05-20 22:17:17 +02:00
|
|
|
_actualFont = { fontFace, 0, fontWeight.Weight, { 0, fontHeight }, CP_UTF8, false };
|
2020-02-12 20:06:46 +01:00
|
|
|
_desiredFont = { _actualFont };
|
|
|
|
|
|
|
|
// set TSF Foreground
|
|
|
|
Media::SolidColorBrush foregroundBrush{};
|
2020-05-16 00:43:00 +02:00
|
|
|
foregroundBrush.Color(static_cast<til::color>(_settings.DefaultForeground()));
|
2020-02-27 01:35:16 +01:00
|
|
|
TSFInputControl().Foreground(foregroundBrush);
|
|
|
|
TSFInputControl().Margin(newMargin);
|
|
|
|
|
|
|
|
// Apply settings for scrollbar
|
|
|
|
if (_settings.ScrollState() == ScrollbarState::Hidden)
|
|
|
|
{
|
ci: run spell check in CI, fix remaining issues (#4799)
This commit introduces a github action to check our spelling and fixes
the following misspelled words so that we come up green.
It also renames TfEditSes to TfEditSession, because Ses is not a word.
currently, excerpt, fallthrough, identified, occurred, propagate,
provided, rendered, resetting, separate, succeeded, successfully,
terminal, transferred, adheres, breaks, combining, preceded,
architecture, populated, previous, setter, visible, window, within,
appxmanifest, hyphen, control, offset, powerpoint, suppress, parsing,
prioritized, aforementioned, check in, build, filling, indices, layout,
mapping, trying, scroll, terabyte, vetoes, viewport, whose
2020-03-25 19:02:53 +01:00
|
|
|
// In the scenario where the user has turned off the OS setting to automatically hide scrollbars, the
|
2020-02-27 01:35:16 +01:00
|
|
|
// Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to
|
|
|
|
// achieve the intended effect.
|
|
|
|
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
|
|
|
|
ScrollBar().Visibility(Visibility::Collapsed);
|
|
|
|
}
|
|
|
|
else // (default or Visible)
|
|
|
|
{
|
|
|
|
// Default behavior
|
|
|
|
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
|
|
|
|
ScrollBar().Visibility(Visibility::Visible);
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-07-14 03:38:11 +02:00
|
|
|
_UpdateSystemParameterSettings();
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Set up each layer's brush used to display the control's background.
|
|
|
|
// - Respects the settings for acrylic, background image and opacity from
|
|
|
|
// _settings.
|
|
|
|
// * If acrylic is not enabled, setup a solid color background, otherwise
|
|
|
|
// use bgcolor as acrylic's tint
|
|
|
|
// - Avoids image flickering and acrylic brush redraw if settings are changed
|
|
|
|
// but the appropriate brush is still in place.
|
|
|
|
// - Does not apply background color outside of acrylic mode;
|
|
|
|
// _BackgroundColorChanged must be called to do so.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void TermControl::_InitializeBackgroundBrush()
|
|
|
|
{
|
|
|
|
if (_settings.UseAcrylic())
|
|
|
|
{
|
|
|
|
// See if we've already got an acrylic background brush
|
|
|
|
// to avoid the flicker when setting up a new one
|
2020-02-27 01:35:16 +01:00
|
|
|
auto acrylic = RootGrid().Background().try_as<Media::AcrylicBrush>();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// Instantiate a brush if there's not already one there
|
|
|
|
if (acrylic == nullptr)
|
|
|
|
{
|
|
|
|
acrylic = Media::AcrylicBrush{};
|
|
|
|
acrylic.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
|
|
|
|
}
|
|
|
|
|
|
|
|
// see GH#1082: Initialize background color so we don't get a
|
|
|
|
// fade/flash when _BackgroundColorChanged is called
|
|
|
|
uint32_t color = _settings.DefaultBackground();
|
|
|
|
winrt::Windows::UI::Color bgColor{};
|
|
|
|
bgColor.R = GetRValue(color);
|
|
|
|
bgColor.G = GetGValue(color);
|
|
|
|
bgColor.B = GetBValue(color);
|
|
|
|
bgColor.A = 255;
|
|
|
|
|
|
|
|
acrylic.FallbackColor(bgColor);
|
|
|
|
acrylic.TintColor(bgColor);
|
|
|
|
|
|
|
|
// Apply brush settings
|
|
|
|
acrylic.TintOpacity(_settings.TintOpacity());
|
|
|
|
|
|
|
|
// Apply brush to control if it's not already there
|
2020-02-27 01:35:16 +01:00
|
|
|
if (RootGrid().Background() != acrylic)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
RootGrid().Background(acrylic);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
2020-04-24 19:16:34 +02:00
|
|
|
|
|
|
|
// GH#5098: Inform the engine of the new opacity of the default text background.
|
|
|
|
if (_renderEngine)
|
|
|
|
{
|
|
|
|
_renderEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.TintOpacity()));
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Media::SolidColorBrush solidColor{};
|
2020-02-27 01:35:16 +01:00
|
|
|
RootGrid().Background(solidColor);
|
2020-04-24 19:16:34 +02:00
|
|
|
|
|
|
|
// GH#5098: Inform the engine of the new opacity of the default text background.
|
|
|
|
if (_renderEngine)
|
|
|
|
{
|
|
|
|
_renderEngine->SetDefaultTextBackgroundOpacity(1.0f);
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!_settings.BackgroundImage().empty())
|
|
|
|
{
|
|
|
|
Windows::Foundation::Uri imageUri{ _settings.BackgroundImage() };
|
|
|
|
|
|
|
|
// Check if the image brush is already pointing to the image
|
|
|
|
// in the modified settings; if it isn't (or isn't there),
|
|
|
|
// set a new image source for the brush
|
2020-02-27 01:35:16 +01:00
|
|
|
auto imageSource = BackgroundImage().Source().try_as<Media::Imaging::BitmapImage>();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
if (imageSource == nullptr ||
|
|
|
|
imageSource.UriSource() == nullptr ||
|
|
|
|
imageSource.UriSource().RawUri() != imageUri.RawUri())
|
|
|
|
{
|
|
|
|
// Note that BitmapImage handles the image load asynchronously,
|
|
|
|
// which is especially important since the image
|
|
|
|
// may well be both large and somewhere out on the
|
|
|
|
// internet.
|
|
|
|
Media::Imaging::BitmapImage image(imageUri);
|
2020-02-27 01:35:16 +01:00
|
|
|
BackgroundImage().Source(image);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply stretch, opacity and alignment settings
|
2020-02-27 01:35:16 +01:00
|
|
|
BackgroundImage().Stretch(_settings.BackgroundImageStretchMode());
|
|
|
|
BackgroundImage().Opacity(_settings.BackgroundImageOpacity());
|
|
|
|
BackgroundImage().HorizontalAlignment(_settings.BackgroundImageHorizontalAlignment());
|
|
|
|
BackgroundImage().VerticalAlignment(_settings.BackgroundImageVerticalAlignment());
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
BackgroundImage().Source(nullptr);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Style the background of the control with the provided background color
|
|
|
|
// Arguments:
|
|
|
|
// - color: The background color to use as a uint32 (aka DWORD COLORREF)
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
2020-05-16 00:43:00 +02:00
|
|
|
winrt::fire_and_forget TermControl::_BackgroundColorChanged(const COLORREF color)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-05-16 00:43:00 +02:00
|
|
|
til::color newBgColor{ color };
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
auto weakThis{ get_weak() };
|
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
co_await winrt::resume_foreground(Dispatcher());
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
if (auto control{ weakThis.get() })
|
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
if (auto acrylic = RootGrid().Background().try_as<Media::AcrylicBrush>())
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-05-16 00:43:00 +02:00
|
|
|
acrylic.FallbackColor(newBgColor);
|
|
|
|
acrylic.TintColor(newBgColor);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
2020-02-27 01:35:16 +01:00
|
|
|
else if (auto solidColor = RootGrid().Background().try_as<Media::SolidColorBrush>())
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-05-16 00:43:00 +02:00
|
|
|
solidColor.Color(newBgColor);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set the default background as transparent to prevent the
|
|
|
|
// DX layer from overwriting the background image or acrylic effect
|
2020-05-16 00:43:00 +02:00
|
|
|
_settings.DefaultBackground(static_cast<COLORREF>(newBgColor.with_alpha(0)));
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TermControl::~TermControl()
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Creates an automation peer for the Terminal Control, enabling accessibility on our control.
|
|
|
|
// Arguments:
|
|
|
|
// - None
|
|
|
|
// Return Value:
|
|
|
|
// - The automation peer for our control
|
|
|
|
Windows::UI::Xaml::Automation::Peers::AutomationPeer TermControl::OnCreateAutomationPeer()
|
|
|
|
try
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_initializedTerminal && !_closing) // only set up the automation peer if we're ready to go live
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
// create a custom automation peer with this code pattern:
|
|
|
|
// (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
|
|
|
|
auto autoPeer = winrt::make_self<winrt::Microsoft::Terminal::TerminalControl::implementation::TermControlAutomationPeer>(this);
|
|
|
|
|
|
|
|
_uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get());
|
|
|
|
_renderer->AddRenderEngine(_uiaEngine.get());
|
|
|
|
return *autoPeer;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
LOG_CAUGHT_EXCEPTION();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
::Microsoft::Console::Types::IUiaData* TermControl::GetUiaData() const
|
|
|
|
{
|
|
|
|
return _terminal.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
const FontInfo TermControl::GetActualFont() const
|
|
|
|
{
|
|
|
|
return _actualFont;
|
|
|
|
}
|
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
const Windows::UI::Xaml::Thickness TermControl::GetPadding()
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
return SwapChainPanel().Margin();
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
TerminalConnection::ConnectionState TermControl::ConnectionState() const
|
|
|
|
{
|
|
|
|
return _connection.State();
|
|
|
|
}
|
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged()
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-04-07 02:15:31 +02:00
|
|
|
// This event is only registered during terminal initialization,
|
|
|
|
// so we don't need to check _initializedTerminal.
|
|
|
|
// We also don't lock for things that come back from the renderer.
|
2020-02-12 20:06:46 +01:00
|
|
|
auto chain = _renderEngine->GetSwapChain();
|
|
|
|
auto weakThis{ get_weak() };
|
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
co_await winrt::resume_foreground(Dispatcher());
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
if (auto control{ weakThis.get() })
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
_AttachDxgiSwapChainToXaml(chain.Get());
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
void TermControl::_AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
|
|
|
|
nativePanel->SetSwapChain(swapChain);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TermControl::_InitializeTerminal()
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
{ // scope for terminalLock
|
|
|
|
auto terminalLock = _terminal->LockForWriting();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_initializedTerminal)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
const auto actualWidth = SwapChainPanel().ActualWidth();
|
|
|
|
const auto actualHeight = SwapChainPanel().ActualHeight();
|
|
|
|
|
|
|
|
const auto windowWidth = actualWidth * SwapChainPanel().CompositionScaleX(); // Width() and Height() are NaN?
|
|
|
|
const auto windowHeight = actualHeight * SwapChainPanel().CompositionScaleY();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
if (windowWidth == 0 || windowHeight == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// First create the render thread.
|
|
|
|
// Then stash a local pointer to the render thread so we can initialize it and enable it
|
|
|
|
// to paint itself *after* we hand off its ownership to the renderer.
|
|
|
|
// We split up construction and initialization of the render thread object this way
|
|
|
|
// because the renderer and render thread have circular references to each other.
|
|
|
|
auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
|
|
|
|
auto* const localPointerToThread = renderThread.get();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Now create the renderer and initialize the render thread.
|
|
|
|
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(_terminal.get(), nullptr, 0, std::move(renderThread));
|
|
|
|
::Microsoft::Console::Render::IRenderTarget& renderTarget = *_renderer;
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-04-14 22:11:47 +02:00
|
|
|
_renderer->SetRendererEnteredErrorStateCallback([weakThis = get_weak()]() {
|
|
|
|
if (auto strongThis{ weakThis.get() })
|
|
|
|
{
|
|
|
|
strongThis->_RendererEnteredErrorState();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Set up the DX Engine
|
|
|
|
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
|
|
|
_renderer->AddRenderEngine(dxEngine.get());
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Initialize our font with the renderer
|
|
|
|
// We don't have to care about DPI. We'll get a change message immediately if it's not 96
|
|
|
|
// and react accordingly.
|
|
|
|
_UpdateFont(true);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
const COORD windowSize{ static_cast<short>(windowWidth), static_cast<short>(windowHeight) };
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Fist set up the dx engine with the window size in pixels.
|
|
|
|
// Then, using the font, get the number of characters that can fit.
|
|
|
|
// Resize our terminal connection to match that size, and initialize the terminal with that size.
|
|
|
|
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
LOG_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Update DxEngine's SelectionBackground
|
|
|
|
dxEngine->SetSelectionBackground(_settings.SelectionBackground());
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
const auto vp = dxEngine->GetViewportInCharacters(viewInPixels);
|
|
|
|
const auto width = vp.Width();
|
|
|
|
const auto height = vp.Height();
|
|
|
|
_connection.Resize(height, width);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Override the default width and height to match the size of the swapChainPanel
|
|
|
|
_settings.InitialCols(width);
|
|
|
|
_settings.InitialRows(height);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
_terminal->CreateFromSettings(_settings, renderTarget);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
dxEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect());
|
Add renderer settings to mitigate blurry text for some graphics devices
## Summary of the Pull Request
Adds user settings to adjust rendering behavior to mitigate blurry text on some devices.
## References
- #778 introduced this, almost certainly.
## PR Checklist
* [x] Closes #5759, mostly
* [x] I work here.
* [ ] We need community verification that this will help.
* [x] Updated schema and schema doc.
* [x] Am core contributor. Discussed in Monday sync meeting and w/ @DHowett-MSFT.
## Detailed Description of the Pull Request / Additional comments
When we switched from full-screen repaints to incremental rendering, it seems like we exposed a situation where some display drivers and hardware combinations do not handle scroll and/or dirty regions (from `IDXGISwapChain::Present1`) without blurring the data from the previous frame. As we're really close to ship, I'm offering two options to let people in this situation escape it on their own. We hope in the future to figure out what's actually going on here and mitigate it further in software, but until then, these escape hatches are available.
1. `experimental.rendering.forceFullRepaint` - This one restores the pre-778 behavior to the Terminal. On every single frame paint, we'll invalidate the entire screen and repaint it.
2. `experimental.rendering.software` - This one uses the software WARP renderer instead of using the hardware and display driver directly. The theory is that this will sidestep any driver bugs or hardware variations.
One, the other, or both of these may be field-applied by users who are experiencing this behavior.
Reverting #778 completely would also resolve this, but it would give back our largest performance win in the whole Terminal project. We don't believe that's acceptable when seemingly a majority of the users are experiencing the performance benefit with no detriment to graphical display.
## Validation Steps Performed
- [x] Flipped them on and verified with the debugger that they are being applied to the rendering pipeline
- [ ] Gave a private copy to community members in #5759 and had them try whether one, the other, or both resolved their issue.
2020-05-11 23:54:03 +02:00
|
|
|
dxEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
|
|
|
|
dxEngine->SetSoftwareRendering(_settings.SoftwareRendering());
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Update DxEngine's AntialiasingMode
|
|
|
|
switch (_settings.AntialiasingMode())
|
|
|
|
{
|
|
|
|
case TextAntialiasingMode::Cleartype:
|
|
|
|
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
|
|
|
|
break;
|
|
|
|
case TextAntialiasingMode::Aliased:
|
|
|
|
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
|
|
|
|
break;
|
|
|
|
case TextAntialiasingMode::Grayscale:
|
|
|
|
default:
|
|
|
|
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
|
|
|
|
break;
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-04-24 19:16:34 +02:00
|
|
|
// GH#5098: Inform the engine of the opacity of the default text background.
|
|
|
|
if (_settings.UseAcrylic())
|
|
|
|
{
|
|
|
|
dxEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.TintOpacity()));
|
|
|
|
}
|
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
THROW_IF_FAILED(dxEngine->Enable());
|
|
|
|
_renderEngine = std::move(dxEngine);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
_AttachDxgiSwapChainToXaml(_renderEngine->GetSwapChain().Get());
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Tell the DX Engine to notify us when the swap chain changes.
|
|
|
|
// We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems)
|
|
|
|
_renderEngine->SetCallback(std::bind(&TermControl::RenderEngineSwapChainChanged, this));
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
auto bottom = _terminal->GetViewport().BottomExclusive();
|
|
|
|
auto bufferHeight = bottom;
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
ScrollBar().Maximum(bufferHeight - bufferHeight);
|
|
|
|
ScrollBar().Minimum(0);
|
|
|
|
ScrollBar().Value(0);
|
|
|
|
ScrollBar().ViewportSize(bufferHeight);
|
2020-08-25 00:54:02 +02:00
|
|
|
ScrollBar().LargeChange(std::max<SHORT>(bufferHeight - 1, 0)); // scroll one "screenful" at a time when the scroll bar is clicked
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
localPointerToThread->EnablePainting();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Set up blinking cursor
|
|
|
|
int blinkTime = GetCaretBlinkTime();
|
|
|
|
if (blinkTime != INFINITE)
|
|
|
|
{
|
|
|
|
// Create a timer
|
|
|
|
DispatcherTimer cursorTimer;
|
|
|
|
cursorTimer.Interval(std::chrono::milliseconds(blinkTime));
|
|
|
|
cursorTimer.Tick({ get_weak(), &TermControl::_CursorTimerTick });
|
|
|
|
cursorTimer.Start();
|
|
|
|
_cursorTimer.emplace(std::move(cursorTimer));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// The user has disabled cursor blinking
|
|
|
|
_cursorTimer = std::nullopt;
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Add support for the "blink" graphic rendition attribute (#7490)
This PR adds support for the _blink_ graphic rendition attribute. When a
character is output with this attribute set, it "blinks" at a regular
interval, by cycling its color between the normal rendition and a dimmer
shade of that color.
The majority of the blinking mechanism is encapsulated in a new
`BlinkingState` class, which is shared between the Terminal and Conhost
implementations. This class keeps track of the position in the blinking
cycle, which determines whether characters are rendered as normal or
faint.
In Windows Terminal, the state is stored in the `Terminal` class, and in
Conhost it's stored in the `CONSOLE_INFORMATION` class. In both cases,
the `IsBlinkingFaint` method is used to determine the current blinking
rendition, and that is passed on as a parameter to the
`TextAttribute::CalculateRgbColors` method when these classes are
looking up attribute colors.
Prior to calculating the colors, the current attribute is also passed to
the `RecordBlinkingUsage` method, which keeps track of whether there are
actually any blink attributes in use. This is used to determine whether
the screen needs to be refreshed when the blinking cycle toggles between
the normal and faint renditions.
The refresh itself is handled by the `ToggleBlinkingRendition` method,
which is triggered by a timer. In Conhost this is just piggybacking on
the existing cursor blink timer, but in Windows Terminal it needs to
have its own separate timer, since the cursor timer is reset whenever a
key is pressed, which is not something we want for attribute blinking.
Although the `ToggleBlinkingRendition` is called at the same rate as the
cursor blinking, we actually only want the cells to blink at half that
frequency. We thus have a counter that cycles through four phases, and
blinking is rendered as faint for two of those four. Then every two
cycles - when the state changes - a redraw is triggered, but only if
there are actually blinking attributes in use (as previously recorded).
As mentioned earlier, the blinking frequency is based on the cursor
blink rate, so that means it'll automatically be disabled if a user has
set their cursor blink rate to none. It can also be disabled by turning
off the _Show animations in Windows_ option. In Conhost these settings
take effect immediately, but in Windows Terminal they only apply when a
new tab is opened.
This PR also adds partial support for the `SGR 6` _rapid blink_
attribute. This is not used by DEC terminals, but was defined in the
ECMA/ANSI standards. It's not widely supported, but many terminals just
it implement it as an alias for the regular `SGR 5` blink attribute, so
that's what I've done here too.
## Validation Steps Performed
I've checked the _Graphic rendition test pattern_ in Vttest, and
compared our representation of the blink attribute to that of an actual
DEC VT220 terminal as seen on [YouTube]. With the right color scheme
it's a reasonably close match.
[YouTube]: https://www.youtube.com/watch?v=03Pz5AmxbE4&t=1m55s
Closes #7388
2020-09-22 01:21:33 +02:00
|
|
|
// Set up blinking attributes
|
|
|
|
BOOL animationsEnabled = TRUE;
|
|
|
|
SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0);
|
|
|
|
if (animationsEnabled && blinkTime != INFINITE)
|
|
|
|
{
|
|
|
|
// Create a timer
|
|
|
|
DispatcherTimer blinkTimer;
|
|
|
|
blinkTimer.Interval(std::chrono::milliseconds(blinkTime));
|
|
|
|
blinkTimer.Tick({ get_weak(), &TermControl::_BlinkTimerTick });
|
|
|
|
blinkTimer.Start();
|
|
|
|
_blinkTimer.emplace(std::move(blinkTimer));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// The user has disabled blinking
|
|
|
|
_blinkTimer = std::nullopt;
|
|
|
|
}
|
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// import value from WinUser (convert from milli-seconds to micro-seconds)
|
|
|
|
_multiClickTimer = GetDoubleClickTime() * 1000;
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// Focus the control here. If we do it during control initialization, then
|
|
|
|
// focus won't actually get passed to us. I believe this is because
|
|
|
|
// we're not technically a part of the UI tree yet, so focusing us
|
|
|
|
// becomes a no-op.
|
|
|
|
this->Focus(FocusState::Programmatic);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
_initializedTerminal = true;
|
|
|
|
} // scope for TerminalLock
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-04-08 22:55:20 +02:00
|
|
|
// Start the connection outside of lock, because it could
|
|
|
|
// start writing output immediately.
|
|
|
|
_connection.Start();
|
|
|
|
|
|
|
|
// Likewise, run the event handlers outside of lock (they could
|
|
|
|
// be reentrant)
|
2020-02-12 20:06:46 +01:00
|
|
|
_InitializedHandlers(*this, nullptr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TermControl::_CharacterHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/,
|
|
|
|
Input::CharacterReceivedRoutedEventArgs const& e)
|
|
|
|
{
|
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto ch = e.Character();
|
2020-04-07 21:09:28 +02:00
|
|
|
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
2020-07-31 19:16:27 +02:00
|
|
|
auto modifiers = _GetPressedModifierKeys();
|
|
|
|
if (e.KeyStatus().IsExtendedKey)
|
|
|
|
{
|
|
|
|
modifiers |= ControlKeyStates::EnhancedKey;
|
|
|
|
}
|
2020-04-07 21:09:28 +02:00
|
|
|
const bool handled = _terminal->SendCharEvent(ch, scanCode, modifiers);
|
2020-02-12 20:06:46 +01:00
|
|
|
e.Handled(handled);
|
|
|
|
}
|
|
|
|
|
2020-03-05 21:35:46 +01:00
|
|
|
// Method Description:
|
2020-06-12 00:41:16 +02:00
|
|
|
// - Manually handles key events for certain keys that can't be passed to us
|
|
|
|
// normally. Namely, the keys we're concerned with are F7 down and Alt up.
|
2020-03-05 21:35:46 +01:00
|
|
|
// Return value:
|
2020-06-12 00:41:16 +02:00
|
|
|
// - Whether the key was handled.
|
2020-08-15 01:44:39 +02:00
|
|
|
bool TermControl::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down)
|
2020-03-05 21:35:46 +01:00
|
|
|
{
|
|
|
|
const auto modifiers{ _GetPressedModifierKeys() };
|
2020-06-12 00:41:16 +02:00
|
|
|
auto handled = false;
|
|
|
|
if (vkey == VK_MENU && !down)
|
2020-03-05 21:35:46 +01:00
|
|
|
{
|
2020-06-12 00:41:16 +02:00
|
|
|
// Manually generate an Alt KeyUp event into the key bindings or terminal.
|
|
|
|
// This is required as part of GH#6421.
|
2020-08-15 01:44:39 +02:00
|
|
|
(void)_TrySendKeyEvent(VK_MENU, scanCode, modifiers, false);
|
2020-03-05 21:35:46 +01:00
|
|
|
handled = true;
|
|
|
|
}
|
2020-06-12 00:41:16 +02:00
|
|
|
else if (vkey == VK_F7 && down)
|
|
|
|
{
|
|
|
|
// Manually generate an F7 event into the key bindings or terminal.
|
|
|
|
// This is required as part of GH#638.
|
|
|
|
auto bindings{ _settings.KeyBindings() };
|
2020-03-05 21:35:46 +01:00
|
|
|
|
2020-06-12 00:41:16 +02:00
|
|
|
if (bindings)
|
|
|
|
{
|
|
|
|
handled = bindings.TryKeyChord({
|
|
|
|
modifiers.IsCtrlPressed(),
|
|
|
|
modifiers.IsAltPressed(),
|
|
|
|
modifiers.IsShiftPressed(),
|
|
|
|
VK_F7,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!handled)
|
|
|
|
{
|
|
|
|
// _TrySendKeyEvent pretends it didn't handle F7 for some unknown reason.
|
2020-08-15 01:44:39 +02:00
|
|
|
(void)_TrySendKeyEvent(VK_F7, scanCode, modifiers, true);
|
2020-06-12 00:41:16 +02:00
|
|
|
// GH#6438: Note that we're _not_ sending the key up here - that'll
|
|
|
|
// get passed through XAML to our KeyUp handler normally.
|
|
|
|
handled = true;
|
|
|
|
}
|
|
|
|
}
|
2020-03-05 21:35:46 +01:00
|
|
|
return handled;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
void TermControl::_KeyDownHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/,
|
|
|
|
Input::KeyRoutedEventArgs const& e)
|
2020-06-09 00:31:28 +02:00
|
|
|
{
|
|
|
|
_KeyHandler(e, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TermControl::_KeyUpHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/,
|
|
|
|
Input::KeyRoutedEventArgs const& e)
|
|
|
|
{
|
|
|
|
_KeyHandler(e, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TermControl::_KeyHandler(Input::KeyRoutedEventArgs const& e, const bool keyDown)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
// If the current focused element is a child element of searchbox,
|
|
|
|
// we do not send this event up to terminal
|
|
|
|
if (_searchBox && _searchBox->ContainsFocus())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-09 00:31:28 +02:00
|
|
|
// Mark the event as handled and do nothing if we're closing, or the key
|
|
|
|
// was the Windows key.
|
|
|
|
//
|
|
|
|
// NOTE: for key combos like CTRL + C, two events are fired (one for
|
|
|
|
// CTRL, one for 'C'). Since it's possible the terminal is in
|
|
|
|
// win32-input-mode, then we'll send all these keystrokes to the
|
|
|
|
// terminal - it's smart enough to ignore the keys it doesn't care
|
|
|
|
// about.
|
2020-02-12 20:06:46 +01:00
|
|
|
if (_closing ||
|
|
|
|
e.OriginalKey() == VirtualKey::LeftWindows ||
|
|
|
|
e.OriginalKey() == VirtualKey::RightWindows)
|
|
|
|
|
|
|
|
{
|
|
|
|
e.Handled(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-31 19:16:27 +02:00
|
|
|
auto modifiers = _GetPressedModifierKeys();
|
2020-02-12 20:06:46 +01:00
|
|
|
const auto vkey = gsl::narrow_cast<WORD>(e.OriginalKey());
|
|
|
|
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
2020-07-31 19:16:27 +02:00
|
|
|
if (e.KeyStatus().IsExtendedKey)
|
|
|
|
{
|
|
|
|
modifiers |= ControlKeyStates::EnhancedKey;
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-06-09 00:31:28 +02:00
|
|
|
// Alt-Numpad# input will send us a character once the user releases
|
|
|
|
// Alt, so we should be ignoring the individual keydowns. The character
|
|
|
|
// will be sent through the TSFInputControl. See GH#1401 for more
|
|
|
|
// details
|
|
|
|
if (modifiers.IsAltPressed() &&
|
|
|
|
(e.OriginalKey() >= VirtualKey::NumberPad0 && e.OriginalKey() <= VirtualKey::NumberPad9))
|
2020-03-18 18:58:10 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
e.Handled(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-09 00:31:28 +02:00
|
|
|
// GH#2235: Terminal::Settings hasn't been modified to differentiate
|
|
|
|
// between AltGr and Ctrl+Alt yet.
|
2020-02-12 20:06:46 +01:00
|
|
|
// -> Don't check for key bindings if this is an AltGr key combination.
|
2020-06-09 00:31:28 +02:00
|
|
|
//
|
|
|
|
// GH#4999: Only process keybindings on the keydown. If we don't check
|
|
|
|
// this at all, we'll process the keybinding twice. If we only process
|
|
|
|
// keybindings on the keyUp, then we'll still send the keydown to the
|
|
|
|
// connected terminal application, and something like ctrl+shift+T will
|
|
|
|
// emit a ^T to the pipe.
|
2020-10-20 01:55:56 +02:00
|
|
|
if (!modifiers.IsAltGrPressed() && keyDown && _TryHandleKeyBinding(vkey, scanCode, modifiers))
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-06-19 00:24:12 +02:00
|
|
|
e.Handled(true);
|
|
|
|
return;
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
2020-06-19 00:24:12 +02:00
|
|
|
if (_TrySendKeyEvent(vkey, scanCode, modifiers, keyDown))
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-06-19 00:24:12 +02:00
|
|
|
e.Handled(true);
|
|
|
|
return;
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Manually prevent keyboard navigation with tab. We want to send tab to
|
|
|
|
// the terminal, and we don't want to be able to escape focus of the
|
|
|
|
// control with tab.
|
2020-06-19 00:24:12 +02:00
|
|
|
e.Handled(e.OriginalKey() == VirtualKey::Tab);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Attempt to handle this key combination as a key binding
|
|
|
|
// Arguments:
|
|
|
|
// - vkey: The vkey of the key pressed.
|
2020-10-20 01:55:56 +02:00
|
|
|
// - scanCode: The scan code of the key pressed.
|
2020-06-19 00:24:12 +02:00
|
|
|
// - modifiers: The ControlKeyStates representing the modifier key states.
|
2020-10-20 01:55:56 +02:00
|
|
|
bool TermControl::_TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const
|
2020-06-19 00:24:12 +02:00
|
|
|
{
|
|
|
|
auto bindings = _settings.KeyBindings();
|
|
|
|
if (!bindings)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-06-19 00:24:12 +02:00
|
|
|
return false;
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
2020-10-20 01:55:56 +02:00
|
|
|
auto success = bindings.TryKeyChord({
|
2020-06-19 00:24:12 +02:00
|
|
|
modifiers.IsCtrlPressed(),
|
|
|
|
modifiers.IsAltPressed(),
|
|
|
|
modifiers.IsShiftPressed(),
|
|
|
|
vkey,
|
|
|
|
});
|
2020-10-20 01:55:56 +02:00
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Let's assume the user has bound the dead key "^" to a sendInput command that sends "b".
|
|
|
|
// If the user presses the two keys "^a" it'll produce "bâ", despite us marking the key event as handled.
|
|
|
|
// The following is used to manually "consume" such dead keys and clear them from the keyboard state.
|
|
|
|
_ClearKeyboardState(vkey, scanCode);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Discards currently pressed dead keys.
|
|
|
|
// Arguments:
|
|
|
|
// - vkey: The vkey of the key pressed.
|
|
|
|
// - scanCode: The scan code of the key pressed.
|
|
|
|
void TermControl::_ClearKeyboardState(const WORD vkey, const WORD scanCode) const noexcept
|
|
|
|
{
|
|
|
|
std::array<BYTE, 256> keyState;
|
|
|
|
if (!GetKeyboardState(keyState.data()))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// As described in "Sometimes you *want* to interfere with the keyboard's state buffer":
|
|
|
|
// http://archives.miloush.net/michkap/archive/2006/09/10/748775.html
|
|
|
|
// > "The key here is to keep trying to pass stuff to ToUnicode until -1 is not returned."
|
|
|
|
std::array<wchar_t, 16> buffer;
|
|
|
|
while (ToUnicodeEx(vkey, scanCode, keyState.data(), buffer.data(), gsl::narrow_cast<int>(buffer.size()), 0b1, nullptr) < 0)
|
|
|
|
{
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Send this particular key event to the terminal.
|
|
|
|
// See Terminal::SendKeyEvent for more information.
|
|
|
|
// - Clears the current selection.
|
|
|
|
// - Makes the cursor briefly visible during typing.
|
|
|
|
// Arguments:
|
|
|
|
// - vkey: The vkey of the key pressed.
|
2020-10-20 01:55:56 +02:00
|
|
|
// - scanCode: The scan code of the key pressed.
|
2020-02-12 20:06:46 +01:00
|
|
|
// - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
|
2020-06-09 00:31:28 +02:00
|
|
|
// - keyDown: If true, the key was pressed, otherwise the key was released.
|
|
|
|
bool TermControl::_TrySendKeyEvent(const WORD vkey,
|
|
|
|
const WORD scanCode,
|
|
|
|
const ControlKeyStates modifiers,
|
|
|
|
const bool keyDown)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
// When there is a selection active, escape should clear it and NOT flow through
|
|
|
|
// to the terminal. With any other keypress, it should clear the selection AND
|
|
|
|
// flow through to the terminal.
|
2020-06-09 23:49:39 +02:00
|
|
|
// GH#6423 - don't dismiss selection if the key that was pressed was a
|
|
|
|
// modifier key. We'll wait for a real keystroke to dismiss the
|
2020-10-16 02:01:01 +02:00
|
|
|
// GH #7395 - don't dismiss selection when taking PrintScreen
|
2020-06-09 23:49:39 +02:00
|
|
|
// selection.
|
2020-10-16 02:01:01 +02:00
|
|
|
if (_terminal->IsSelectionActive() && !KeyEvent::IsModifierKey(vkey) && vkey != VK_SNAPSHOT)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
_terminal->ClearSelection();
|
2020-02-21 00:03:50 +01:00
|
|
|
_renderer->TriggerSelection();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
if (vkey == VK_ESCAPE)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 21:01:01 +01:00
|
|
|
if (vkey == VK_ESCAPE ||
|
|
|
|
vkey == VK_RETURN)
|
|
|
|
{
|
|
|
|
TSFInputControl().ClearBuffer();
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// If the terminal translated the key, mark the event as handled.
|
|
|
|
// This will prevent the system from trying to get the character out
|
|
|
|
// of it and sending us a CharacterReceived event.
|
2020-06-09 00:31:28 +02:00
|
|
|
const auto handled = vkey ? _terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown) : true;
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
if (_cursorTimer.has_value())
|
|
|
|
{
|
|
|
|
// Manually show the cursor when a key is pressed. Restarting
|
|
|
|
// the timer prevents flickering.
|
2020-03-13 18:39:42 +01:00
|
|
|
_terminal->SetCursorOn(true);
|
2020-02-12 20:06:46 +01:00
|
|
|
_cursorTimer.value().Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
return handled;
|
|
|
|
}
|
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
// Method Description:
|
|
|
|
// - handle a tap event by taking focus
|
|
|
|
// Arguments:
|
|
|
|
// - sender: the XAML element responding to the tap event
|
|
|
|
// - args: event data
|
|
|
|
void TermControl::_TappedHandler(const IInspectable& /*sender*/, const TappedRoutedEventArgs& e)
|
|
|
|
{
|
|
|
|
Focus(FocusState::Pointer);
|
|
|
|
e.Handled(true);
|
|
|
|
}
|
|
|
|
|
2020-03-13 01:44:28 +01:00
|
|
|
// Method Description:
|
|
|
|
// - Send this particular mouse event to the terminal.
|
|
|
|
// See Terminal::SendMouseEvent for more information.
|
|
|
|
// Arguments:
|
|
|
|
// - point: the PointerPoint object representing a mouse event from our XAML input handler
|
|
|
|
bool TermControl::_TrySendMouseEvent(Windows::UI::Input::PointerPoint const& point)
|
|
|
|
{
|
|
|
|
const auto props = point.Properties();
|
|
|
|
|
|
|
|
// Get the terminal position relative to the viewport
|
|
|
|
const auto terminalPosition = _GetTerminalPosition(point.Position());
|
|
|
|
|
|
|
|
// Which mouse button changed state (and how)
|
|
|
|
unsigned int uiButton{};
|
|
|
|
switch (props.PointerUpdateKind())
|
|
|
|
{
|
|
|
|
case PointerUpdateKind::LeftButtonPressed:
|
|
|
|
uiButton = WM_LBUTTONDOWN;
|
|
|
|
break;
|
|
|
|
case PointerUpdateKind::LeftButtonReleased:
|
|
|
|
uiButton = WM_LBUTTONUP;
|
|
|
|
break;
|
|
|
|
case PointerUpdateKind::MiddleButtonPressed:
|
|
|
|
uiButton = WM_MBUTTONDOWN;
|
|
|
|
break;
|
|
|
|
case PointerUpdateKind::MiddleButtonReleased:
|
|
|
|
uiButton = WM_MBUTTONUP;
|
|
|
|
break;
|
|
|
|
case PointerUpdateKind::RightButtonPressed:
|
|
|
|
uiButton = WM_RBUTTONDOWN;
|
|
|
|
break;
|
|
|
|
case PointerUpdateKind::RightButtonReleased:
|
|
|
|
uiButton = WM_RBUTTONUP;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
uiButton = WM_MOUSEMOVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mouse wheel data
|
|
|
|
const short sWheelDelta = ::base::saturated_cast<short>(props.MouseWheelDelta());
|
|
|
|
if (sWheelDelta != 0 && !props.IsHorizontalMouseWheel())
|
|
|
|
{
|
|
|
|
// if we have a mouse wheel delta and it wasn't a horizontal wheel motion
|
|
|
|
uiButton = WM_MOUSEWHEEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto modifiers = _GetPressedModifierKeys();
|
2020-08-08 01:21:09 +02:00
|
|
|
const TerminalInput::MouseButtonState state{ props.IsLeftButtonPressed(), props.IsMiddleButtonPressed(), props.IsRightButtonPressed() };
|
|
|
|
return _terminal->SendMouseEvent(terminalPosition, uiButton, modifiers, sWheelDelta, state);
|
2020-03-13 01:44:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Checks if we can send vt mouse input.
|
|
|
|
// Arguments:
|
|
|
|
// - point: the PointerPoint object representing a mouse event from our XAML input handler
|
|
|
|
bool TermControl::_CanSendVTMouseInput()
|
|
|
|
{
|
Process actions sync. on startup; don't dupe nonexistent profile (#5090)
This PR has evolved to encapsulate two related fixes that I can't really
untie anymore.
#2455 - Duplicating a tab that doesn't exist anymore
This was the bug I was originally fixing in #4429.
When the user tries to `duplicateTab` with a profile that doesn't exist
anymore (like might happen after a settings reload), don't crash.
As I was going about adding tests for this, got blocked by the fact that
the Terminal couldn't open _any_ panes while the `TerminalPage` was size
0x0. This had two theoretical solutions:
* Fake the `TerminalPage` into thinking it had a real size in the test -
probably possible, though I'm unsure how it would work in practice.
* Change `Pane`s to not require an `ActualWidth`, `ActualHeight` on
initialization.
Fortuately, the second option was something else that was already on my
backlog of bugs.
#4618 - `wt` command-line can't consistently parse more than one arg
Presently, the Terminal just arbitrarily dispatches a bunch of handlers
to try and handle all the commands provided on the commandline. That's
lead to a bunch of reports that not all the commands will always get
executed, nor will they all get executed in the same order.
This PR also changes the `TerminalPage` to be able to dispatch all the
commands sequentially, all at once in the startup. No longer will there
be a hot second where the commands seem to execute themselves in from of
the user - they'll all happen behind the scenes on startup.
This involved a couple other changes areound the `TerminalPage`
* I had to make sure that panes could be opened at a 0x0 size. Now they
use a star sizing based off the percentage of the parent they're
supposed to consume, so that when the parent _does_ get laid out,
they'll take the appropriate size of that parent.
* I had to do some math ahead of time to try and calculate what a
`SplitState::Automatic` would be evaluated as, despite the fact that
we don't actually know how big the pane will be.
* I had to ensure that `focus-tab` commands appropriately mark a single
tab as focused while we're in startup, without roundtripping to the
Dispatcher thread and back
## References
#4429 - the original PR for #2455
#5047 - a follow-up task from discussion in #4429
#4953 - a PR for making panes use star sizing, which was immensly
helpful for this PR.
## Detailed Description of the Pull Request / Additional comments
`CascadiaSettings::BuildSettings` can throw if the GUID doesn't exist.
This wraps those calls up with a try/catch.
It also adds a couple tests - a few `SettingsTests` for try/catching
this state. It also adds a XAML-y test in `TabTests` that creates a
`TerminalPage` and then performs som UI-like actions on it. This test
required a minor change to how we generate the new tab dropdown - in the
tests, `Application::Current()` is _not_ a `TerminalApp::App`, so it
doesn't have a `Logic()` to query. So wrap that in a try/catch as well.
While working on these tests, I found that we'd crash pretty agressively
for mysterious reasons if the TestHostApp became focused while the test
was running. This was due to a call in
`TSFInputControl::NotifyFocusEnter` that would callback to
`TSFInputControl::_layoutRequested`, which would crash on setting the
`MaxSize` of the canvas to a negative value. This PR includes a hotfix
for that bug as well.
## Validation Steps Performed
* Manual testing with a _lot_ of commands in a commandline
* run the tests
* Team tested in selfhost
Closes #2455
Closes #4618
2020-03-26 01:03:32 +01:00
|
|
|
if (!_terminal)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2020-03-13 01:44:28 +01:00
|
|
|
// If the user is holding down Shift, suppress mouse events
|
|
|
|
// TODO GH#4875: disable/customize this functionality
|
|
|
|
const auto modifiers = _GetPressedModifierKeys();
|
|
|
|
if (modifiers.IsShiftPressed())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return _terminal->IsTrackingMouseInput();
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Method Description:
|
|
|
|
// - handle a mouse click event. Begin selection process.
|
|
|
|
// Arguments:
|
|
|
|
// - sender: the XAML element responding to the pointer input
|
|
|
|
// - args: event data
|
|
|
|
void TermControl::_PointerPressedHandler(Windows::Foundation::IInspectable const& sender,
|
|
|
|
Input::PointerRoutedEventArgs const& args)
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
_CapturePointer(sender, args);
|
|
|
|
|
|
|
|
const auto ptr = args.Pointer();
|
2020-02-27 01:35:16 +01:00
|
|
|
const auto point = args.GetCurrentPoint(*this);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-10 18:55:57 +01:00
|
|
|
// We also TryShow in GotFocusHandler, but this call is specifically
|
|
|
|
// for the case where the Terminal is in focus but the user closed the
|
|
|
|
// on-screen keyboard. This lets the user simply tap on the terminal
|
|
|
|
// again to bring it up.
|
|
|
|
InputPane::GetForCurrentView().TryShow();
|
|
|
|
|
2020-02-13 01:32:50 +01:00
|
|
|
if (!_focused)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-02-13 01:32:50 +01:00
|
|
|
Focus(FocusState::Pointer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
|
|
|
|
{
|
2020-02-12 20:06:46 +01:00
|
|
|
const auto modifiers = static_cast<uint32_t>(args.KeyModifiers());
|
|
|
|
// static_cast to a uint32_t because we can't use the WI_IsFlagSet
|
|
|
|
// macro directly with a VirtualKeyModifiers
|
|
|
|
const auto altEnabled = WI_IsFlagSet(modifiers, static_cast<uint32_t>(VirtualKeyModifiers::Menu));
|
|
|
|
const auto shiftEnabled = WI_IsFlagSet(modifiers, static_cast<uint32_t>(VirtualKeyModifiers::Shift));
|
2020-09-03 19:52:39 +02:00
|
|
|
const auto ctrlEnabled = WI_IsFlagSet(modifiers, static_cast<uint32_t>(VirtualKeyModifiers::Control));
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-13 01:44:28 +01:00
|
|
|
if (_CanSendVTMouseInput())
|
|
|
|
{
|
|
|
|
_TrySendMouseEvent(point);
|
|
|
|
args.Handled(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
if (point.Properties().IsLeftButtonPressed())
|
|
|
|
{
|
2020-04-13 22:09:02 +02:00
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
const auto cursorPosition = point.Position();
|
|
|
|
const auto terminalPosition = _GetTerminalPosition(cursorPosition);
|
|
|
|
|
|
|
|
// handle ALT key
|
2020-02-28 01:42:26 +01:00
|
|
|
_terminal->SetBlockSelection(altEnabled);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
auto clickCount = _NumberOfClicks(cursorPosition, point.Timestamp());
|
|
|
|
|
|
|
|
// This formula enables the number of clicks to cycle properly between single-, double-, and triple-click.
|
|
|
|
// To increase the number of acceptable click states, simply increment MAX_CLICK_COUNT and add another if-statement
|
|
|
|
const unsigned int MAX_CLICK_COUNT = 3;
|
|
|
|
const auto multiClickMapper = clickCount > MAX_CLICK_COUNT ? ((clickCount + MAX_CLICK_COUNT - 1) % MAX_CLICK_COUNT) + 1 : clickCount;
|
|
|
|
|
2020-06-25 02:47:13 +02:00
|
|
|
::Terminal::SelectionExpansionMode mode = ::Terminal::SelectionExpansionMode::Cell;
|
|
|
|
if (multiClickMapper == 1)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-06-25 02:47:13 +02:00
|
|
|
mode = ::Terminal::SelectionExpansionMode::Cell;
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
else if (multiClickMapper == 2)
|
|
|
|
{
|
2020-06-25 02:47:13 +02:00
|
|
|
mode = ::Terminal::SelectionExpansionMode::Word;
|
|
|
|
}
|
|
|
|
else if (multiClickMapper == 3)
|
|
|
|
{
|
|
|
|
mode = ::Terminal::SelectionExpansionMode::Line;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the selection appropriately
|
2020-09-03 19:52:39 +02:00
|
|
|
if (ctrlEnabled && multiClickMapper == 1 &&
|
|
|
|
!(_terminal->GetHyperlinkAtPosition(terminalPosition).empty()))
|
|
|
|
{
|
|
|
|
_HyperlinkHandler(_terminal->GetHyperlinkAtPosition(terminalPosition));
|
|
|
|
}
|
|
|
|
else if (shiftEnabled && _terminal->IsSelectionActive())
|
2020-06-25 02:47:13 +02:00
|
|
|
{
|
|
|
|
// Shift+Click: only set expand on the "end" selection point
|
|
|
|
_terminal->SetSelectionEnd(terminalPosition, mode);
|
2020-03-25 22:09:49 +01:00
|
|
|
_selectionNeedsToBeCopied = true;
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
2020-06-25 02:47:13 +02:00
|
|
|
else if (mode == ::Terminal::SelectionExpansionMode::Cell)
|
|
|
|
{
|
|
|
|
// Single Click: reset the selection and begin a new one
|
|
|
|
_terminal->ClearSelection();
|
|
|
|
_singleClickTouchdownPos = cursorPosition;
|
|
|
|
_selectionNeedsToBeCopied = false; // there's no selection, so there's nothing to update
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
else
|
|
|
|
{
|
2020-06-25 02:47:13 +02:00
|
|
|
// Multi-Click Selection: expand both "start" and "end" selection points
|
|
|
|
_terminal->MultiClickSelection(terminalPosition, mode);
|
|
|
|
_selectionNeedsToBeCopied = true;
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
2020-04-13 22:09:02 +02:00
|
|
|
|
2020-06-25 02:47:13 +02:00
|
|
|
_lastMouseClickTimestamp = point.Timestamp();
|
|
|
|
_lastMouseClickPos = cursorPosition;
|
2020-02-12 20:06:46 +01:00
|
|
|
_renderer->TriggerSelection();
|
|
|
|
}
|
|
|
|
else if (point.Properties().IsRightButtonPressed())
|
|
|
|
{
|
2020-03-10 15:59:16 +01:00
|
|
|
// CopyOnSelect right click always pastes
|
2020-03-25 22:09:49 +01:00
|
|
|
if (_settings.CopyOnSelect() || !_terminal->IsSelectionActive())
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
PasteTextFromClipboard();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
Add copyFormatting keybinding arg and array support (#6004)
Adds array support for the existing `copyFormatting` global setting.
This allows users to define which formats they would specifically like
to be copied.
A boolean value is still accepted and is translated to the following:
- `false` --> `"none"` or `[]`
- `true` --> `"all"` or `["html", "rtf"]`
This also adds `copyFormatting` as a keybinding arg for `copy`. As with
the global setting, a boolean value and array value is accepted.
CopyFormat is a WinRT enum where each accepted format is a flag.
Currently accepted formats include `html`, and `rtf`. A boolean value is
accepted and converted. `true` is a conjunction of all the formats.
`false` only includes plain text.
For the global setting, `null` is not accepted. We already have a
default value from before so no worries there.
For the keybinding arg, `null` (the default value) means that we just do
what the global arg says to do. Overall, the `copyFormatting` keybinding
arg is an override of the global setting **when using that keybinding**.
References #5212 - Spec for formatted copying
References #2690 - disable html copy
Validated behavior with every combination of values below:
- `copyFormatting` global: { `true`, `false`, `[]`, `["html"]` }
- `copyFormatting` copy arg:
{ `null`, `true`, `false`, `[]`, `[, "html"]`}
Closes #4191
Closes #5262
2020-08-15 03:02:24 +02:00
|
|
|
CopySelectionToClipboard(shiftEnabled, nullptr);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
|
|
|
|
{
|
|
|
|
const auto contactRect = point.Properties().ContactRect();
|
|
|
|
// Set our touch rect, to start a pan.
|
|
|
|
_touchAnchor = winrt::Windows::Foundation::Point{ contactRect.X, contactRect.Y };
|
|
|
|
}
|
|
|
|
|
|
|
|
args.Handled(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - handle a mouse moved event. Specifically handling mouse drag to update selection process.
|
|
|
|
// Arguments:
|
|
|
|
// - sender: not used
|
|
|
|
// - args: event data
|
|
|
|
void TermControl::_PointerMovedHandler(Windows::Foundation::IInspectable const& /*sender*/,
|
|
|
|
Input::PointerRoutedEventArgs const& args)
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
const auto ptr = args.Pointer();
|
2020-02-27 01:35:16 +01:00
|
|
|
const auto point = args.GetCurrentPoint(*this);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-13 01:44:28 +01:00
|
|
|
if (!_focused)
|
|
|
|
{
|
|
|
|
args.Handled(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
|
|
|
|
{
|
2020-03-13 01:44:28 +01:00
|
|
|
if (_CanSendVTMouseInput())
|
|
|
|
{
|
|
|
|
_TrySendMouseEvent(point);
|
|
|
|
args.Handled(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
if (point.Properties().IsLeftButtonPressed())
|
|
|
|
{
|
2020-04-13 22:09:02 +02:00
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
|
2020-03-25 22:09:49 +01:00
|
|
|
const auto cursorPosition = point.Position();
|
2020-02-20 19:30:10 +01:00
|
|
|
|
2020-03-25 22:09:49 +01:00
|
|
|
if (_singleClickTouchdownPos)
|
2020-02-13 01:32:50 +01:00
|
|
|
{
|
2020-03-25 22:09:49 +01:00
|
|
|
// Figure out if the user's moved a quarter of a cell's smaller axis away from the clickdown point
|
|
|
|
auto& touchdownPoint{ *_singleClickTouchdownPos };
|
|
|
|
auto distance{ std::sqrtf(std::powf(cursorPosition.X - touchdownPoint.X, 2) + std::powf(cursorPosition.Y - touchdownPoint.Y, 2)) };
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
const til::size fontSize{ _actualFont.GetSize() };
|
|
|
|
|
|
|
|
const auto fontSizeInDips = fontSize.scale(til::math::rounding, 1.0f / _renderEngine->GetScaling());
|
|
|
|
if (distance >= (std::min(fontSizeInDips.width(), fontSizeInDips.height()) / 4.f))
|
2020-03-25 22:09:49 +01:00
|
|
|
{
|
|
|
|
_terminal->SetSelectionAnchor(_GetTerminalPosition(touchdownPoint));
|
|
|
|
// stop tracking the touchdown point
|
|
|
|
_singleClickTouchdownPos = std::nullopt;
|
|
|
|
}
|
2020-02-13 01:32:50 +01:00
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
_SetEndSelectionPointAtCursor(cursorPosition);
|
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
const double cursorBelowBottomDist = cursorPosition.Y - SwapChainPanel().Margin().Top - SwapChainPanel().ActualHeight();
|
|
|
|
const double cursorAboveTopDist = -1 * cursorPosition.Y + SwapChainPanel().Margin().Top;
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
constexpr double MinAutoScrollDist = 2.0; // Arbitrary value
|
|
|
|
double newAutoScrollVelocity = 0.0;
|
|
|
|
if (cursorBelowBottomDist > MinAutoScrollDist)
|
|
|
|
{
|
|
|
|
newAutoScrollVelocity = _GetAutoScrollSpeed(cursorBelowBottomDist);
|
|
|
|
}
|
|
|
|
else if (cursorAboveTopDist > MinAutoScrollDist)
|
|
|
|
{
|
|
|
|
newAutoScrollVelocity = -1.0 * _GetAutoScrollSpeed(cursorAboveTopDist);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newAutoScrollVelocity != 0)
|
|
|
|
{
|
|
|
|
_TryStartAutoScroll(point, newAutoScrollVelocity);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_TryStopAutoScroll(ptr.PointerId());
|
|
|
|
}
|
|
|
|
}
|
2020-09-10 23:59:56 +02:00
|
|
|
const auto terminalPos = _GetTerminalPosition(point.Position());
|
|
|
|
if (terminalPos != _lastHoveredCell)
|
|
|
|
{
|
|
|
|
const auto uri = _terminal->GetHyperlinkAtPosition(terminalPos);
|
|
|
|
if (!uri.empty())
|
|
|
|
{
|
|
|
|
// Update the tooltip with the URI
|
|
|
|
HoveredUri().Text(uri);
|
|
|
|
|
|
|
|
// 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 Thickness newThickness{ wtInDips, htInDips, 0, 0 };
|
|
|
|
HyperlinkTooltipBorder().BorderThickness(newThickness);
|
|
|
|
|
|
|
|
// Compute the location of the top left corner of the cell in DIPS
|
|
|
|
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
|
|
|
|
const til::point startPos{ terminalPos.X, terminalPos.Y };
|
|
|
|
const til::size fontSize{ _actualFont.GetSize() };
|
|
|
|
const til::point posInPixels{ startPos * fontSize };
|
|
|
|
const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() };
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
_lastHoveredCell = terminalPos;
|
|
|
|
|
|
|
|
const auto newId = _terminal->GetHyperlinkIdAtPosition(terminalPos);
|
2020-10-28 21:24:43 +01:00
|
|
|
const auto newInterval = _terminal->GetHyperlinkIntervalFromPosition(terminalPos);
|
|
|
|
// If the hyperlink ID changed or the interval changed, trigger a redraw all
|
|
|
|
// (so this will happen both when we move onto a link and when we move off a link)
|
|
|
|
if (newId != _lastHoveredId || (newInterval != _lastHoveredInterval))
|
2020-09-10 23:59:56 +02:00
|
|
|
{
|
2020-10-28 21:24:43 +01:00
|
|
|
_lastHoveredId = newId;
|
|
|
|
_lastHoveredInterval = newInterval;
|
2020-09-10 23:59:56 +02:00
|
|
|
_renderEngine->UpdateHyperlinkHoveredId(newId);
|
2020-10-28 21:24:43 +01:00
|
|
|
_renderer->UpdateLastHoveredInterval(newInterval);
|
2020-09-10 23:59:56 +02:00
|
|
|
_renderer->TriggerRedrawAll();
|
|
|
|
}
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch && _touchAnchor)
|
|
|
|
{
|
|
|
|
const auto contactRect = point.Properties().ContactRect();
|
|
|
|
winrt::Windows::Foundation::Point newTouchPoint{ contactRect.X, contactRect.Y };
|
|
|
|
const auto anchor = _touchAnchor.value();
|
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// Our _actualFont's size is in pixels, convert to DIPs, which the
|
|
|
|
// rest of the Points here are in.
|
|
|
|
const til::size fontSize{ _actualFont.GetSize() };
|
|
|
|
const auto fontSizeInDips = fontSize.scale(til::math::rounding, 1.0f / _renderEngine->GetScaling());
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// Get the difference between the point we've dragged to and the start of the touch.
|
2020-02-12 20:06:46 +01:00
|
|
|
const float dy = newTouchPoint.Y - anchor.Y;
|
|
|
|
|
2020-02-19 02:07:05 +01:00
|
|
|
// Start viewport scroll after we've moved more than a half row of text
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
if (std::abs(dy) > (fontSizeInDips.height<float>() / 2.0f))
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
// 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)
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
const float numRows = -1.0f * (dy / fontSizeInDips.height<float>());
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
const auto currentOffset = ::base::ClampedNumeric<double>(ScrollBar().Value());
|
2020-02-19 02:07:05 +01:00
|
|
|
const auto newValue = numRows + currentOffset;
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
ScrollBar().Value(newValue);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// Use this point as our new scroll anchor.
|
|
|
|
_touchAnchor = newTouchPoint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
args.Handled(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Event handler for the PointerReleased event. We use this to de-anchor
|
|
|
|
// touch events, to stop scrolling via touch.
|
|
|
|
// Arguments:
|
|
|
|
// - sender: the XAML element responding to the pointer input
|
|
|
|
// - args: event data
|
|
|
|
void TermControl::_PointerReleasedHandler(Windows::Foundation::IInspectable const& sender,
|
|
|
|
Input::PointerRoutedEventArgs const& args)
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
const auto ptr = args.Pointer();
|
2020-03-10 15:59:16 +01:00
|
|
|
const auto point = args.GetCurrentPoint(*this);
|
|
|
|
|
|
|
|
_ReleasePointerCapture(sender, args);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
|
|
|
|
{
|
2020-03-13 01:44:28 +01:00
|
|
|
if (_CanSendVTMouseInput())
|
|
|
|
{
|
|
|
|
_TrySendMouseEvent(point);
|
|
|
|
args.Handled(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-10 15:59:16 +01:00
|
|
|
// Only a left click release when copy on select is active should perform a copy.
|
|
|
|
// Right clicks and middle clicks should not need to do anything when released.
|
2020-04-14 22:19:22 +02:00
|
|
|
if (_settings.CopyOnSelect() && point.Properties().PointerUpdateKind() == Windows::UI::Input::PointerUpdateKind::LeftButtonReleased && _selectionNeedsToBeCopied)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
Add copyFormatting keybinding arg and array support (#6004)
Adds array support for the existing `copyFormatting` global setting.
This allows users to define which formats they would specifically like
to be copied.
A boolean value is still accepted and is translated to the following:
- `false` --> `"none"` or `[]`
- `true` --> `"all"` or `["html", "rtf"]`
This also adds `copyFormatting` as a keybinding arg for `copy`. As with
the global setting, a boolean value and array value is accepted.
CopyFormat is a WinRT enum where each accepted format is a flag.
Currently accepted formats include `html`, and `rtf`. A boolean value is
accepted and converted. `true` is a conjunction of all the formats.
`false` only includes plain text.
For the global setting, `null` is not accepted. We already have a
default value from before so no worries there.
For the keybinding arg, `null` (the default value) means that we just do
what the global arg says to do. Overall, the `copyFormatting` keybinding
arg is an override of the global setting **when using that keybinding**.
References #5212 - Spec for formatted copying
References #2690 - disable html copy
Validated behavior with every combination of values below:
- `copyFormatting` global: { `true`, `false`, `[]`, `["html"]` }
- `copyFormatting` copy arg:
{ `null`, `true`, `false`, `[]`, `[, "html"]`}
Closes #4191
Closes #5262
2020-08-15 03:02:24 +02:00
|
|
|
CopySelectionToClipboard(false, nullptr);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
|
|
|
|
{
|
|
|
|
_touchAnchor = std::nullopt;
|
|
|
|
}
|
|
|
|
|
2020-03-25 22:09:49 +01:00
|
|
|
_singleClickTouchdownPos = std::nullopt;
|
2020-02-20 19:30:10 +01:00
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
_TryStopAutoScroll(ptr.PointerId());
|
|
|
|
|
|
|
|
args.Handled(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Event handler for the PointerWheelChanged event. This is raised in
|
|
|
|
// response to mouse wheel changes. Depending upon what modifier keys are
|
|
|
|
// pressed, different actions will take place.
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
// - Primarily just takes the data from the PointerRoutedEventArgs and uses
|
|
|
|
// it to call _DoMouseWheel, see _DoMouseWheel for more details.
|
2020-02-12 20:06:46 +01:00
|
|
|
// Arguments:
|
|
|
|
// - args: the event args containing information about t`he mouse wheel event.
|
|
|
|
void TermControl::_MouseWheelHandler(Windows::Foundation::IInspectable const& /*sender*/,
|
|
|
|
Input::PointerRoutedEventArgs const& args)
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
const auto point = args.GetCurrentPoint(*this);
|
2020-08-08 01:21:09 +02:00
|
|
|
const auto props = point.Properties();
|
|
|
|
const TerminalInput::MouseButtonState state{ props.IsLeftButtonPressed(), props.IsMiddleButtonPressed(), props.IsRightButtonPressed() };
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
auto result = _DoMouseWheel(point.Position(),
|
|
|
|
ControlKeyStates{ args.KeyModifiers() },
|
|
|
|
point.Properties().MouseWheelDelta(),
|
2020-08-08 01:21:09 +02:00
|
|
|
state);
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
if (result)
|
2020-03-13 01:44:28 +01:00
|
|
|
{
|
|
|
|
args.Handled(true);
|
|
|
|
}
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
}
|
2020-03-13 01:44:28 +01:00
|
|
|
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
// Method Description:
|
|
|
|
// - Actually handle a scrolling event, whether from a mouse wheel or a
|
|
|
|
|
|
|
|
// touchpad scroll. Depending upon what modifier keys are pressed,
|
|
|
|
// different actions will take place.
|
|
|
|
// * Attempts to first dispatch the mouse scroll as a VT event
|
|
|
|
// * If Ctrl+Shift are pressed, then attempts to change our opacity
|
|
|
|
// * If just Ctrl is pressed, we'll attempt to "zoom" by changing our font size
|
|
|
|
// * Otherwise, just scrolls the content of the viewport
|
|
|
|
// Arguments:
|
|
|
|
// - point: the location of the mouse during this event
|
|
|
|
// - modifiers: The modifiers pressed during this event, in the form of a VirtualKeyModifiers
|
|
|
|
// - delta: the mouse wheel delta that triggered this event.
|
|
|
|
bool TermControl::_DoMouseWheel(const Windows::Foundation::Point point,
|
|
|
|
const ControlKeyStates modifiers,
|
|
|
|
const int32_t delta,
|
2020-08-08 01:21:09 +02:00
|
|
|
const TerminalInput::MouseButtonState state)
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
{
|
|
|
|
if (_CanSendVTMouseInput())
|
|
|
|
{
|
|
|
|
// Most mouse event handlers call
|
|
|
|
// _TrySendMouseEvent(point);
|
|
|
|
// here with a PointerPoint. However, as of #979, we don't have a
|
|
|
|
// PointerPoint to work with. So, we're just going to do a
|
|
|
|
// mousewheel event manually
|
|
|
|
return _terminal->SendMouseEvent(_GetTerminalPosition(point),
|
|
|
|
WM_MOUSEWHEEL,
|
|
|
|
_GetPressedModifierKeys(),
|
2020-08-08 01:21:09 +02:00
|
|
|
::base::saturated_cast<short>(delta),
|
|
|
|
state);
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
const auto ctrlPressed = modifiers.IsCtrlPressed();
|
|
|
|
const auto shiftPressed = modifiers.IsShiftPressed();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
if (ctrlPressed && shiftPressed)
|
|
|
|
{
|
|
|
|
_MouseTransparencyHandler(delta);
|
|
|
|
}
|
|
|
|
else if (ctrlPressed)
|
|
|
|
{
|
|
|
|
_MouseZoomHandler(delta);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-08-08 01:21:09 +02:00
|
|
|
_MouseScrollHandler(delta, point, state.isLeftButtonDown);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - This is part of the solution to GH#979
|
|
|
|
// - Manually handle a scrolling event. This is used to help support
|
|
|
|
// scrolling on devices where the touchpad doesn't correctly handle
|
|
|
|
// scrolling inactive windows.
|
|
|
|
// Arguments:
|
|
|
|
// - location: the location of the mouse during this event. This location is
|
|
|
|
// relative to the origin of the control
|
|
|
|
// - delta: the mouse wheel delta that triggered this event.
|
2020-08-08 01:21:09 +02:00
|
|
|
// - state: the state for each of the mouse buttons individually (pressed/unpressed)
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
bool TermControl::OnMouseWheel(const Windows::Foundation::Point location,
|
2020-08-08 01:21:09 +02:00
|
|
|
const int32_t delta,
|
|
|
|
const bool leftButtonDown,
|
|
|
|
const bool midButtonDown,
|
|
|
|
const bool rightButtonDown)
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
{
|
|
|
|
const auto modifiers = _GetPressedModifierKeys();
|
2020-08-08 01:21:09 +02:00
|
|
|
TerminalInput::MouseButtonState state{ leftButtonDown, midButtonDown, rightButtonDown };
|
|
|
|
return _DoMouseWheel(location, modifiers, delta, state);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
2020-10-28 21:24:43 +01:00
|
|
|
// Method Description:
|
|
|
|
// - Tell TerminalCore to update its knowledge about the locations of visible regex patterns
|
|
|
|
// - We should call this (through the throttled function) when something causes the visible
|
|
|
|
// region to change, such as when new text enters the buffer or the viewport is scrolled
|
|
|
|
void TermControl::UpdatePatternLocations()
|
|
|
|
{
|
|
|
|
_terminal->UpdatePatterns();
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Method Description:
|
|
|
|
// - Adjust the opacity of the acrylic background in response to a mouse
|
|
|
|
// scrolling event.
|
|
|
|
// Arguments:
|
|
|
|
// - mouseDelta: the mouse wheel delta that triggered this event.
|
|
|
|
void TermControl::_MouseTransparencyHandler(const double mouseDelta)
|
|
|
|
{
|
|
|
|
// Transparency is on a scale of [0.0,1.0], so only increment by .01.
|
|
|
|
const auto effectiveDelta = mouseDelta < 0 ? -.01 : .01;
|
|
|
|
|
|
|
|
if (_settings.UseAcrylic())
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
auto acrylicBrush = RootGrid().Background().as<Media::AcrylicBrush>();
|
2020-04-24 19:16:34 +02:00
|
|
|
_settings.TintOpacity(acrylicBrush.TintOpacity() + effectiveDelta);
|
|
|
|
acrylicBrush.TintOpacity(_settings.TintOpacity());
|
|
|
|
|
2020-03-11 18:48:58 +01:00
|
|
|
if (acrylicBrush.TintOpacity() == 1.0)
|
|
|
|
{
|
|
|
|
_settings.UseAcrylic(false);
|
|
|
|
_InitializeBackgroundBrush();
|
2020-05-16 00:43:00 +02:00
|
|
|
COLORREF bg = _settings.DefaultBackground();
|
2020-03-11 18:48:58 +01:00
|
|
|
_BackgroundColorChanged(bg);
|
|
|
|
}
|
2020-04-24 19:16:34 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// GH#5098: Inform the engine of the new opacity of the default text background.
|
|
|
|
if (_renderEngine)
|
|
|
|
{
|
|
|
|
_renderEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.TintOpacity()));
|
|
|
|
}
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
CATCH_LOG();
|
|
|
|
}
|
2020-03-11 18:48:58 +01:00
|
|
|
else if (mouseDelta < 0)
|
|
|
|
{
|
|
|
|
_settings.UseAcrylic(true);
|
|
|
|
|
|
|
|
//Setting initial opacity set to 1 to ensure smooth transition to acrylic during mouse scroll
|
|
|
|
_settings.TintOpacity(1.0);
|
|
|
|
_InitializeBackgroundBrush();
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Adjust the font size of the terminal in response to a mouse scrolling
|
|
|
|
// event.
|
|
|
|
// Arguments:
|
|
|
|
// - mouseDelta: the mouse wheel delta that triggered this event.
|
|
|
|
void TermControl::_MouseZoomHandler(const double mouseDelta)
|
|
|
|
{
|
|
|
|
const auto fontDelta = mouseDelta < 0 ? -1 : 1;
|
|
|
|
AdjustFontSize(fontDelta);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Reset the font size of the terminal to its default size.
|
|
|
|
// Arguments:
|
|
|
|
// - none
|
|
|
|
void TermControl::ResetFontSize()
|
|
|
|
{
|
|
|
|
_SetFontSize(_settings.FontSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Adjust the font size of the terminal control.
|
|
|
|
// Arguments:
|
|
|
|
// - fontSizeDelta: The amount to increase or decrease the font size by.
|
|
|
|
void TermControl::AdjustFontSize(int fontSizeDelta)
|
|
|
|
{
|
|
|
|
const auto newSize = _desiredFont.GetEngineSize().Y + fontSizeDelta;
|
|
|
|
_SetFontSize(newSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Scroll the visible viewport in response to a mouse wheel event.
|
|
|
|
// Arguments:
|
|
|
|
// - mouseDelta: the mouse wheel delta that triggered this event.
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
// - point: the location of the mouse during this event
|
|
|
|
// - isLeftButtonPressed: true iff the left mouse button was pressed during this event.
|
|
|
|
void TermControl::_MouseScrollHandler(const double mouseDelta,
|
|
|
|
const Windows::Foundation::Point point,
|
|
|
|
const bool isLeftButtonPressed)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
const auto currentOffset = ScrollBar().Value();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// negative = down, positive = up
|
|
|
|
// However, for us, the signs are flipped.
|
2020-07-14 03:38:11 +02:00
|
|
|
// With one of the precision mice, one click is always a multiple of 120 (WHEEL_DELTA),
|
2020-02-12 20:06:46 +01:00
|
|
|
// but the "smooth scrolling" mode results in non-int values
|
2020-07-14 03:38:11 +02:00
|
|
|
const auto rowDelta = mouseDelta / (-1.0 * WHEEL_DELTA);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-07-14 03:38:11 +02:00
|
|
|
// WHEEL_PAGESCROLL is a Win32 constant that represents the "scroll one page
|
|
|
|
// at a time" setting. If we ignore it, we will scroll a truly absurd number
|
|
|
|
// of rows.
|
|
|
|
const auto rowsToScroll{ _rowsToScroll == WHEEL_PAGESCROLL ? GetViewHeight() : _rowsToScroll };
|
|
|
|
double newValue = (rowsToScroll * rowDelta) + (currentOffset);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// The scroll bar's ValueChanged handler will actually move the viewport
|
|
|
|
// for us.
|
2020-02-27 01:35:16 +01:00
|
|
|
ScrollBar().Value(newValue);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
if (_terminal->IsSelectionActive() && isLeftButtonPressed)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-04-25 00:14:43 +02:00
|
|
|
// Have to take the lock or we could change the endpoints out from under the renderer actively rendering.
|
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// If user is mouse selecting and scrolls, they then point at new character.
|
|
|
|
// Make sure selection reflects that immediately.
|
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request
As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).
This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.
Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal.
## References
* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML
## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`
## Validation Steps Performed
* It works on my HP Spectre 2017 with a synaptics trackpad
- I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 18:58:16 +02:00
|
|
|
_SetEndSelectionPointAtCursor(point);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& /*sender*/,
|
|
|
|
Controls::Primitives::RangeBaseValueChangedEventArgs const& args)
|
|
|
|
{
|
2020-06-12 21:51:37 +02:00
|
|
|
if (_isInternalScrollBarUpdate || _closing)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-06-12 21:51:37 +02:00
|
|
|
// The update comes from ourselves, more specifically from the
|
|
|
|
// terminal. So we don't have to update the terminal because it
|
|
|
|
// already knows.
|
2020-02-12 20:06:46 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-28 21:24:43 +01:00
|
|
|
// Clear the regex pattern tree so the renderer does not try to render them while scrolling
|
|
|
|
_terminal->ClearPatternTree();
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
const auto newValue = static_cast<int>(args.NewValue());
|
|
|
|
|
|
|
|
// This is a scroll event that wasn't initiated by the terminal
|
|
|
|
// itself - it was initiated by the mouse wheel, or the scrollbar.
|
|
|
|
_terminal->UserScrollViewport(newValue);
|
|
|
|
|
2020-06-12 21:51:37 +02:00
|
|
|
// User input takes priority over terminal events so cancel
|
|
|
|
// any pending scroll bar update if the user scrolls.
|
|
|
|
_updateScrollBar->ModifyPending([](auto& update) {
|
|
|
|
update.newValue.reset();
|
|
|
|
});
|
2020-10-28 21:24:43 +01:00
|
|
|
|
|
|
|
_updatePatternLocations->Run();
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - captures the pointer so that none of the other XAML elements respond to pointer events
|
|
|
|
// Arguments:
|
|
|
|
// - sender: XAML element that is interacting with pointer
|
|
|
|
// - args: pointer data (i.e.: mouse, touch)
|
|
|
|
// Return Value:
|
|
|
|
// - true if we successfully capture the pointer, false otherwise.
|
|
|
|
bool TermControl::_CapturePointer(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& args)
|
|
|
|
{
|
|
|
|
IUIElement uielem;
|
|
|
|
if (sender.try_as(uielem))
|
|
|
|
{
|
|
|
|
uielem.CapturePointer(args.Pointer());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - releases the captured pointer because we're done responding to XAML pointer events
|
|
|
|
// Arguments:
|
|
|
|
// - sender: XAML element that is interacting with pointer
|
|
|
|
// - args: pointer data (i.e.: mouse, touch)
|
|
|
|
// Return Value:
|
|
|
|
// - true if we release capture of the pointer, false otherwise.
|
|
|
|
bool TermControl::_ReleasePointerCapture(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& args)
|
|
|
|
{
|
|
|
|
IUIElement uielem;
|
|
|
|
if (sender.try_as(uielem))
|
|
|
|
{
|
|
|
|
uielem.ReleasePointerCapture(args.Pointer());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Starts new pointer related auto scroll behavior, or continues existing one.
|
|
|
|
// Does nothing when there is already auto scroll associated with another pointer.
|
|
|
|
// Arguments:
|
|
|
|
// - pointerPoint: info about pointer that causes auto scroll. Pointer's position
|
|
|
|
// is later used to update selection.
|
|
|
|
// - scrollVelocity: target velocity of scrolling in characters / sec
|
|
|
|
void TermControl::_TryStartAutoScroll(Windows::UI::Input::PointerPoint const& pointerPoint, const double scrollVelocity)
|
|
|
|
{
|
|
|
|
// Allow only one pointer at the time
|
|
|
|
if (!_autoScrollingPointerPoint.has_value() || _autoScrollingPointerPoint.value().PointerId() == pointerPoint.PointerId())
|
|
|
|
{
|
|
|
|
_autoScrollingPointerPoint = pointerPoint;
|
|
|
|
_autoScrollVelocity = scrollVelocity;
|
|
|
|
|
|
|
|
// If this is first time the auto scroll update is about to be called,
|
|
|
|
// kick-start it by initializing its time delta as if it started now
|
|
|
|
if (!_lastAutoScrollUpdateTime.has_value())
|
|
|
|
{
|
|
|
|
_lastAutoScrollUpdateTime = std::chrono::high_resolution_clock::now();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apparently this check is not necessary but greatly improves performance
|
|
|
|
if (!_autoScrollTimer.IsEnabled())
|
|
|
|
{
|
|
|
|
_autoScrollTimer.Start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Stops auto scroll if it's active and is associated with supplied pointer id.
|
|
|
|
// Arguments:
|
|
|
|
// - pointerId: id of pointer for which to stop auto scroll
|
|
|
|
void TermControl::_TryStopAutoScroll(const uint32_t pointerId)
|
|
|
|
{
|
|
|
|
if (_autoScrollingPointerPoint.has_value() && pointerId == _autoScrollingPointerPoint.value().PointerId())
|
|
|
|
{
|
|
|
|
_autoScrollingPointerPoint = std::nullopt;
|
|
|
|
_autoScrollVelocity = 0;
|
|
|
|
_lastAutoScrollUpdateTime = std::nullopt;
|
|
|
|
|
|
|
|
// Apparently this check is not necessary but greatly improves performance
|
|
|
|
if (_autoScrollTimer.IsEnabled())
|
|
|
|
{
|
|
|
|
_autoScrollTimer.Stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Called continuously to gradually scroll viewport when user is
|
|
|
|
// mouse selecting outside it (to 'follow' the cursor).
|
|
|
|
// Arguments:
|
|
|
|
// - none
|
|
|
|
void TermControl::_UpdateAutoScroll(Windows::Foundation::IInspectable const& /* sender */,
|
|
|
|
Windows::Foundation::IInspectable const& /* e */)
|
|
|
|
{
|
|
|
|
if (_autoScrollVelocity != 0)
|
|
|
|
{
|
|
|
|
const auto timeNow = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
if (_lastAutoScrollUpdateTime.has_value())
|
|
|
|
{
|
|
|
|
static constexpr double microSecPerSec = 1000000.0;
|
|
|
|
const double deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(timeNow - _lastAutoScrollUpdateTime.value()).count() / microSecPerSec;
|
2020-02-27 01:35:16 +01:00
|
|
|
ScrollBar().Value(ScrollBar().Value() + _autoScrollVelocity * deltaTime);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
if (_autoScrollingPointerPoint.has_value())
|
|
|
|
{
|
2020-04-25 00:14:43 +02:00
|
|
|
// Have to take the lock because the renderer will not draw correctly if you move its endpoints while it is generating a frame.
|
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
_SetEndSelectionPointAtCursor(_autoScrollingPointerPoint.value().Position());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_lastAutoScrollUpdateTime = timeNow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Event handler for the GotFocus event. This is used to...
|
|
|
|
// - enable accessibility notifications for this TermControl
|
|
|
|
// - start blinking the cursor when the window is focused
|
|
|
|
// - update the number of lines to scroll to the value set in the system
|
|
|
|
void TermControl::_GotFocusHandler(Windows::Foundation::IInspectable const& /* sender */,
|
|
|
|
RoutedEventArgs const& /* args */)
|
|
|
|
{
|
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
Allow IME input in Search Box (#4723)
## Summary of the Pull Request
Currently, when the user attempts to type using IME while the Search Box is focused, the input goes to the terminal instead. This is due to the fact that the `TSFInputControl` assumes it's in control whenever TermControl gets focus. So, it'll intercept IME input before the Search Box receives it. We simply need to modify `TermControl::GotFocus` to check if the SearchBox has focus. If it does, `TSFInputControl::NotifyFocusEnter` shouldn't be called.
As a small side fix, I've also disabled the terminal cursor blinking when the Search Box has focus.
Thinking a little further, if we have more features in the future that behave like search box (i.e. advanced tab switcher, or any other XAML controls that pop up) and require text input, we might need to create a sort of "AnyOtherTextControlInFocus" function to see if TSFInputControl should receive focus or not.
## PR Checklist
* [x] Closes #4434
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed
## Validation Steps Performed
Search works as expected with IME. Composition picker appears underneath Search Box when typing IME in the Search Box. Clicking outside of the Search Box still returns control to TSFInputControl/TermControl.
Terminal Cursor blinks when it has focus, and doesn't when the Search Box has focus.
2020-02-26 20:28:01 +01:00
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
_focused = true;
|
|
|
|
|
2020-03-10 18:55:57 +01:00
|
|
|
InputPane::GetForCurrentView().TryShow();
|
|
|
|
|
2020-04-29 19:36:45 +02:00
|
|
|
// GH#5421: Enable the UiaEngine before checking for the SearchBox
|
|
|
|
// That way, new selections are notified to automation clients.
|
|
|
|
if (_uiaEngine.get())
|
|
|
|
{
|
|
|
|
THROW_IF_FAILED(_uiaEngine->Enable());
|
|
|
|
}
|
|
|
|
|
Allow IME input in Search Box (#4723)
## Summary of the Pull Request
Currently, when the user attempts to type using IME while the Search Box is focused, the input goes to the terminal instead. This is due to the fact that the `TSFInputControl` assumes it's in control whenever TermControl gets focus. So, it'll intercept IME input before the Search Box receives it. We simply need to modify `TermControl::GotFocus` to check if the SearchBox has focus. If it does, `TSFInputControl::NotifyFocusEnter` shouldn't be called.
As a small side fix, I've also disabled the terminal cursor blinking when the Search Box has focus.
Thinking a little further, if we have more features in the future that behave like search box (i.e. advanced tab switcher, or any other XAML controls that pop up) and require text input, we might need to create a sort of "AnyOtherTextControlInFocus" function to see if TSFInputControl should receive focus or not.
## PR Checklist
* [x] Closes #4434
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed
## Validation Steps Performed
Search works as expected with IME. Composition picker appears underneath Search Box when typing IME in the Search Box. Clicking outside of the Search Box still returns control to TSFInputControl/TermControl.
Terminal Cursor blinks when it has focus, and doesn't when the Search Box has focus.
2020-02-26 20:28:01 +01:00
|
|
|
// If the searchbox is focused, we don't want TSFInputControl to think
|
|
|
|
// it has focus so it doesn't intercept IME input. We also don't want the
|
|
|
|
// terminal's cursor to start blinking. So, we'll just return quickly here.
|
|
|
|
if (_searchBox && _searchBox->ContainsFocus())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
if (TSFInputControl() != nullptr)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
TSFInputControl().NotifyFocusEnter();
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_cursorTimer.has_value())
|
|
|
|
{
|
|
|
|
// When the terminal focuses, show the cursor immediately
|
2020-03-13 18:39:42 +01:00
|
|
|
_terminal->SetCursorOn(true);
|
2020-02-12 20:06:46 +01:00
|
|
|
_cursorTimer.value().Start();
|
|
|
|
}
|
2020-07-14 03:38:11 +02:00
|
|
|
|
Add support for the "blink" graphic rendition attribute (#7490)
This PR adds support for the _blink_ graphic rendition attribute. When a
character is output with this attribute set, it "blinks" at a regular
interval, by cycling its color between the normal rendition and a dimmer
shade of that color.
The majority of the blinking mechanism is encapsulated in a new
`BlinkingState` class, which is shared between the Terminal and Conhost
implementations. This class keeps track of the position in the blinking
cycle, which determines whether characters are rendered as normal or
faint.
In Windows Terminal, the state is stored in the `Terminal` class, and in
Conhost it's stored in the `CONSOLE_INFORMATION` class. In both cases,
the `IsBlinkingFaint` method is used to determine the current blinking
rendition, and that is passed on as a parameter to the
`TextAttribute::CalculateRgbColors` method when these classes are
looking up attribute colors.
Prior to calculating the colors, the current attribute is also passed to
the `RecordBlinkingUsage` method, which keeps track of whether there are
actually any blink attributes in use. This is used to determine whether
the screen needs to be refreshed when the blinking cycle toggles between
the normal and faint renditions.
The refresh itself is handled by the `ToggleBlinkingRendition` method,
which is triggered by a timer. In Conhost this is just piggybacking on
the existing cursor blink timer, but in Windows Terminal it needs to
have its own separate timer, since the cursor timer is reset whenever a
key is pressed, which is not something we want for attribute blinking.
Although the `ToggleBlinkingRendition` is called at the same rate as the
cursor blinking, we actually only want the cells to blink at half that
frequency. We thus have a counter that cycles through four phases, and
blinking is rendered as faint for two of those four. Then every two
cycles - when the state changes - a redraw is triggered, but only if
there are actually blinking attributes in use (as previously recorded).
As mentioned earlier, the blinking frequency is based on the cursor
blink rate, so that means it'll automatically be disabled if a user has
set their cursor blink rate to none. It can also be disabled by turning
off the _Show animations in Windows_ option. In Conhost these settings
take effect immediately, but in Windows Terminal they only apply when a
new tab is opened.
This PR also adds partial support for the `SGR 6` _rapid blink_
attribute. This is not used by DEC terminals, but was defined in the
ECMA/ANSI standards. It's not widely supported, but many terminals just
it implement it as an alias for the regular `SGR 5` blink attribute, so
that's what I've done here too.
## Validation Steps Performed
I've checked the _Graphic rendition test pattern_ in Vttest, and
compared our representation of the blink attribute to that of an actual
DEC VT220 terminal as seen on [YouTube]. With the right color scheme
it's a reasonably close match.
[YouTube]: https://www.youtube.com/watch?v=03Pz5AmxbE4&t=1m55s
Closes #7388
2020-09-22 01:21:33 +02:00
|
|
|
if (_blinkTimer.has_value())
|
|
|
|
{
|
|
|
|
_blinkTimer.value().Start();
|
|
|
|
}
|
|
|
|
|
2020-07-14 03:38:11 +02:00
|
|
|
_UpdateSystemParameterSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description
|
|
|
|
// - Updates internal params based on system parameters
|
|
|
|
void TermControl::_UpdateSystemParameterSettings() noexcept
|
|
|
|
{
|
|
|
|
if (!SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &_rowsToScroll, 0))
|
|
|
|
{
|
|
|
|
LOG_LAST_ERROR();
|
|
|
|
// If SystemParametersInfoW fails, which it shouldn't, fall back to
|
|
|
|
// Windows' default value.
|
|
|
|
_rowsToScroll = 3;
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Event handler for the LostFocus event. This is used to...
|
|
|
|
// - disable accessibility notifications for this TermControl
|
|
|
|
// - hide and stop blinking the cursor when the window loses focus.
|
|
|
|
void TermControl::_LostFocusHandler(Windows::Foundation::IInspectable const& /* sender */,
|
|
|
|
RoutedEventArgs const& /* args */)
|
|
|
|
{
|
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2020-03-27 00:25:11 +01:00
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
_focused = false;
|
|
|
|
|
|
|
|
if (_uiaEngine.get())
|
|
|
|
{
|
|
|
|
THROW_IF_FAILED(_uiaEngine->Disable());
|
|
|
|
}
|
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
if (TSFInputControl() != nullptr)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
TSFInputControl().NotifyFocusLeave();
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_cursorTimer.has_value())
|
|
|
|
{
|
|
|
|
_cursorTimer.value().Stop();
|
2020-03-13 18:39:42 +01:00
|
|
|
_terminal->SetCursorOn(false);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
Add support for the "blink" graphic rendition attribute (#7490)
This PR adds support for the _blink_ graphic rendition attribute. When a
character is output with this attribute set, it "blinks" at a regular
interval, by cycling its color between the normal rendition and a dimmer
shade of that color.
The majority of the blinking mechanism is encapsulated in a new
`BlinkingState` class, which is shared between the Terminal and Conhost
implementations. This class keeps track of the position in the blinking
cycle, which determines whether characters are rendered as normal or
faint.
In Windows Terminal, the state is stored in the `Terminal` class, and in
Conhost it's stored in the `CONSOLE_INFORMATION` class. In both cases,
the `IsBlinkingFaint` method is used to determine the current blinking
rendition, and that is passed on as a parameter to the
`TextAttribute::CalculateRgbColors` method when these classes are
looking up attribute colors.
Prior to calculating the colors, the current attribute is also passed to
the `RecordBlinkingUsage` method, which keeps track of whether there are
actually any blink attributes in use. This is used to determine whether
the screen needs to be refreshed when the blinking cycle toggles between
the normal and faint renditions.
The refresh itself is handled by the `ToggleBlinkingRendition` method,
which is triggered by a timer. In Conhost this is just piggybacking on
the existing cursor blink timer, but in Windows Terminal it needs to
have its own separate timer, since the cursor timer is reset whenever a
key is pressed, which is not something we want for attribute blinking.
Although the `ToggleBlinkingRendition` is called at the same rate as the
cursor blinking, we actually only want the cells to blink at half that
frequency. We thus have a counter that cycles through four phases, and
blinking is rendered as faint for two of those four. Then every two
cycles - when the state changes - a redraw is triggered, but only if
there are actually blinking attributes in use (as previously recorded).
As mentioned earlier, the blinking frequency is based on the cursor
blink rate, so that means it'll automatically be disabled if a user has
set their cursor blink rate to none. It can also be disabled by turning
off the _Show animations in Windows_ option. In Conhost these settings
take effect immediately, but in Windows Terminal they only apply when a
new tab is opened.
This PR also adds partial support for the `SGR 6` _rapid blink_
attribute. This is not used by DEC terminals, but was defined in the
ECMA/ANSI standards. It's not widely supported, but many terminals just
it implement it as an alias for the regular `SGR 5` blink attribute, so
that's what I've done here too.
## Validation Steps Performed
I've checked the _Graphic rendition test pattern_ in Vttest, and
compared our representation of the blink attribute to that of an actual
DEC VT220 terminal as seen on [YouTube]. With the right color scheme
it's a reasonably close match.
[YouTube]: https://www.youtube.com/watch?v=03Pz5AmxbE4&t=1m55s
Closes #7388
2020-09-22 01:21:33 +02:00
|
|
|
|
|
|
|
if (_blinkTimer.has_value())
|
|
|
|
{
|
|
|
|
_blinkTimer.value().Stop();
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
2020-08-12 15:46:53 +02:00
|
|
|
// - Writes the given sequence as input to the active terminal connection.
|
|
|
|
// - This method has been overloaded to allow zero-copy winrt::param::hstring optimizations.
|
2020-02-12 20:06:46 +01:00
|
|
|
// Arguments:
|
|
|
|
// - wstr: the string of characters to write to the terminal connection.
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
2020-08-12 15:46:53 +02:00
|
|
|
void TermControl::_SendInputToConnection(const winrt::hstring& wstr)
|
|
|
|
{
|
|
|
|
_connection.WriteInput(wstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TermControl::_SendInputToConnection(std::wstring_view wstr)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
_connection.WriteInput(wstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Pre-process text pasted (presumably from the clipboard)
|
|
|
|
// before sending it over the terminal's connection, converting
|
|
|
|
// Windows-space \r\n line-endings to \r line-endings
|
|
|
|
void TermControl::_SendPastedTextToConnection(const std::wstring& wstr)
|
|
|
|
{
|
|
|
|
// Some notes on this implementation:
|
|
|
|
//
|
|
|
|
// - std::regex can do this in a single line, but is somewhat
|
|
|
|
// overkill for a simple search/replace operation (and its
|
|
|
|
// performance guarantees aren't exactly stellar)
|
|
|
|
// - The STL doesn't have a simple string search/replace method.
|
|
|
|
// This fact is lamentable.
|
|
|
|
// - This line-ending conversion is intentionally fairly
|
|
|
|
// conservative, to avoid stripping out lone \n characters
|
|
|
|
// where they could conceivably be intentional.
|
|
|
|
|
|
|
|
std::wstring stripped{ wstr };
|
|
|
|
|
|
|
|
std::wstring::size_type pos = 0;
|
|
|
|
|
|
|
|
while ((pos = stripped.find(L"\r\n", pos)) != std::wstring::npos)
|
|
|
|
{
|
|
|
|
stripped.replace(pos, 2, L"\r");
|
|
|
|
}
|
|
|
|
|
|
|
|
_connection.WriteInput(stripped);
|
|
|
|
_terminal->TrySnapOnInput();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Update the font with the renderer. This will be called either when the
|
|
|
|
// font changes or the DPI changes, as DPI changes will necessitate a
|
|
|
|
// font change. This method will *not* change the buffer/viewport size
|
|
|
|
// to account for the new glyph dimensions. Callers should make sure to
|
2020-04-30 02:05:29 +02:00
|
|
|
// appropriately call _DoResizeUnderLock after this method is called.
|
|
|
|
// - The write lock should be held when calling this method.
|
2020-02-12 20:06:46 +01:00
|
|
|
// Arguments:
|
|
|
|
// - initialUpdate: whether this font update should be considered as being
|
|
|
|
// concerned with initialization process. Value forwarded to event handler.
|
|
|
|
void TermControl::_UpdateFont(const bool initialUpdate)
|
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
const int newDpi = static_cast<int>(static_cast<double>(USER_DEFAULT_SCREEN_DPI) * SwapChainPanel().CompositionScaleX());
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
|
|
|
|
// actually fail. We need a way to gracefully fallback.
|
|
|
|
_renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont);
|
|
|
|
|
|
|
|
const auto actualNewSize = _actualFont.GetSize();
|
|
|
|
_fontSizeChangedHandlers(actualNewSize.X, actualNewSize.Y, initialUpdate);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Set the font size of the terminal control.
|
|
|
|
// Arguments:
|
|
|
|
// - fontSize: The size of the font.
|
|
|
|
void TermControl::_SetFontSize(int fontSize)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Make sure we have a non-zero font size
|
2020-02-26 02:10:07 +01:00
|
|
|
const auto newSize = std::max<short>(gsl::narrow_cast<short>(fontSize), 1);
|
2020-02-12 20:06:46 +01:00
|
|
|
const auto fontFace = _settings.FontFace();
|
2020-05-20 22:17:17 +02:00
|
|
|
const auto fontWeight = _settings.FontWeight();
|
|
|
|
_actualFont = { fontFace, 0, fontWeight.Weight, { 0, newSize }, CP_UTF8, false };
|
2020-02-12 20:06:46 +01:00
|
|
|
_desiredFont = { _actualFont };
|
|
|
|
|
2020-04-30 02:05:29 +02:00
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Refresh our font with the renderer
|
|
|
|
_UpdateFont();
|
2020-03-27 00:25:11 +01:00
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Resize the terminal's BUFFER to match the new font size. This does
|
|
|
|
// NOT change the size of the window, because that can lead to more
|
|
|
|
// problems (like what happens when you change the font size while the
|
|
|
|
// window is maximized?)
|
2020-04-30 02:05:29 +02:00
|
|
|
_RefreshSizeUnderLock();
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
CATCH_LOG();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Triggered when the swapchain changes size. We use this to resize the
|
|
|
|
// terminal buffers to match the new visible size.
|
|
|
|
// Arguments:
|
|
|
|
// - e: a SizeChangedEventArgs with the new dimensions of the SwapChainPanel
|
|
|
|
void TermControl::_SwapChainSizeChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/,
|
|
|
|
SizeChangedEventArgs const& e)
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (!_initializedTerminal || _closing)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
const auto newSize = e.NewSize();
|
|
|
|
const auto currentScaleX = SwapChainPanel().CompositionScaleX();
|
|
|
|
const auto currentEngineScale = _renderEngine->GetScaling();
|
|
|
|
auto foundationSize = newSize;
|
|
|
|
|
|
|
|
// A strange thing can happen here. If you have two tabs open, and drag
|
|
|
|
// across a DPI boundary, then switch to the other tab, that tab will
|
|
|
|
// receive two events: First, a SizeChanged, then a ScaleChanged. In the
|
|
|
|
// SizeChanged event handler, the SwapChainPanel's CompositionScale will
|
|
|
|
// _already_ be the new scaling, but the engine won't have that value
|
|
|
|
// yet. If we scale by the CompositionScale here, we'll end up in a
|
|
|
|
// weird torn state. I'm not totally sure why.
|
|
|
|
//
|
|
|
|
// Fortunately we will be getting that following ScaleChanged event, and
|
|
|
|
// we'll end up resizing again, so we don't terribly need to worry about
|
|
|
|
// this.
|
|
|
|
foundationSize.Width *= currentEngineScale;
|
|
|
|
foundationSize.Height *= currentEngineScale;
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-04-30 02:05:29 +02:00
|
|
|
_DoResizeUnderLock(foundationSize.Width, foundationSize.Height);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// Method Description:
|
|
|
|
// - Triggered when the swapchain changes DPI. When this happens, we're
|
|
|
|
// going to receive 3 events:
|
|
|
|
// - 1. First, a CompositionScaleChanged _for the original scale_. I don't
|
|
|
|
// know why this event happens first. **It also doesn't always happen.**
|
|
|
|
// However, when it does happen, it doesn't give us any useful
|
|
|
|
// information.
|
|
|
|
// - 2. Then, a SizeChanged. During that SizeChanged, either:
|
|
|
|
// - the CompositionScale will still be the original DPI. This happens
|
|
|
|
// when the control is visible as the DPI changes.
|
|
|
|
// - The CompositionScale will be the new DPI. This happens when the
|
|
|
|
// control wasn't focused as the window's DPI changed, so it only got
|
|
|
|
// these messages after XAML updated it's scaling.
|
|
|
|
// - 3. Finally, a CompositionScaleChanged with the _new_ DPI.
|
|
|
|
// - 4. We'll usually get another SizeChanged some time after this last
|
|
|
|
// ScaleChanged. This usually seems to happen after something triggers
|
|
|
|
// the UI to re-layout, like hovering over the scrollbar. This event
|
|
|
|
// doesn't reliably happen immediately after a scale change, so we can't
|
|
|
|
// depend on it (despite the fact that both the scale and size state is
|
|
|
|
// definitely correct in it)
|
|
|
|
// - In the 3rd event, we're going to update our font size for the new DPI.
|
|
|
|
// At that point, we know how big the font should be for the new DPI, and
|
|
|
|
// how big the SwapChainPanel will be. If these sizes are different, we'll
|
|
|
|
// need to resize the buffer to fit in the new window.
|
|
|
|
// Arguments:
|
|
|
|
// - sender: The SwapChainPanel who's DPI changed. This is our _swapchainPanel.
|
|
|
|
// - args: This param is unused in the CompositionScaleChanged event.
|
2020-02-12 20:06:46 +01:00
|
|
|
void TermControl::_SwapChainScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
|
|
|
|
Windows::Foundation::IInspectable const& /*args*/)
|
|
|
|
{
|
2020-02-13 01:42:06 +01:00
|
|
|
if (_renderEngine)
|
|
|
|
{
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
const auto scaleX = sender.CompositionScaleX();
|
|
|
|
const auto scaleY = sender.CompositionScaleY();
|
|
|
|
const auto dpi = (float)(scaleX * USER_DEFAULT_SCREEN_DPI);
|
|
|
|
const auto currentEngineScale = _renderEngine->GetScaling();
|
|
|
|
|
|
|
|
// If we're getting a notification to change to the DPI we already
|
|
|
|
// have, then we're probably just beginning the DPI change. Since
|
|
|
|
// we'll get _another_ event with the real DPI, do nothing here for
|
|
|
|
// now. We'll also skip the next resize in _SwapChainSizeChanged.
|
|
|
|
const bool dpiWasUnchanged = currentEngineScale == scaleX;
|
|
|
|
if (dpiWasUnchanged)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
const auto actualFontOldSize = _actualFont.GetSize();
|
|
|
|
|
2020-04-30 02:05:29 +02:00
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
_renderer->TriggerFontChange(::base::saturated_cast<int>(dpi), _desiredFont, _actualFont);
|
|
|
|
|
|
|
|
const auto actualFontNewSize = _actualFont.GetSize();
|
|
|
|
if (actualFontNewSize != actualFontOldSize)
|
|
|
|
{
|
2020-04-30 02:05:29 +02:00
|
|
|
_RefreshSizeUnderLock();
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
}
|
2020-02-13 01:42:06 +01:00
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Toggle the cursor on and off when called by the cursor blink timer.
|
|
|
|
// Arguments:
|
|
|
|
// - sender: not used
|
|
|
|
// - e: not used
|
2020-03-27 00:25:11 +01:00
|
|
|
void TermControl::_CursorTimerTick(Windows::Foundation::IInspectable const& /* sender */,
|
|
|
|
Windows::Foundation::IInspectable const& /* e */)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
if ((_closing) || (!_terminal->IsCursorBlinkingAllowed() && _terminal->IsCursorVisible()))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2020-03-13 18:39:42 +01:00
|
|
|
_terminal->SetCursorOn(!_terminal->IsCursorOn());
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
Add support for the "blink" graphic rendition attribute (#7490)
This PR adds support for the _blink_ graphic rendition attribute. When a
character is output with this attribute set, it "blinks" at a regular
interval, by cycling its color between the normal rendition and a dimmer
shade of that color.
The majority of the blinking mechanism is encapsulated in a new
`BlinkingState` class, which is shared between the Terminal and Conhost
implementations. This class keeps track of the position in the blinking
cycle, which determines whether characters are rendered as normal or
faint.
In Windows Terminal, the state is stored in the `Terminal` class, and in
Conhost it's stored in the `CONSOLE_INFORMATION` class. In both cases,
the `IsBlinkingFaint` method is used to determine the current blinking
rendition, and that is passed on as a parameter to the
`TextAttribute::CalculateRgbColors` method when these classes are
looking up attribute colors.
Prior to calculating the colors, the current attribute is also passed to
the `RecordBlinkingUsage` method, which keeps track of whether there are
actually any blink attributes in use. This is used to determine whether
the screen needs to be refreshed when the blinking cycle toggles between
the normal and faint renditions.
The refresh itself is handled by the `ToggleBlinkingRendition` method,
which is triggered by a timer. In Conhost this is just piggybacking on
the existing cursor blink timer, but in Windows Terminal it needs to
have its own separate timer, since the cursor timer is reset whenever a
key is pressed, which is not something we want for attribute blinking.
Although the `ToggleBlinkingRendition` is called at the same rate as the
cursor blinking, we actually only want the cells to blink at half that
frequency. We thus have a counter that cycles through four phases, and
blinking is rendered as faint for two of those four. Then every two
cycles - when the state changes - a redraw is triggered, but only if
there are actually blinking attributes in use (as previously recorded).
As mentioned earlier, the blinking frequency is based on the cursor
blink rate, so that means it'll automatically be disabled if a user has
set their cursor blink rate to none. It can also be disabled by turning
off the _Show animations in Windows_ option. In Conhost these settings
take effect immediately, but in Windows Terminal they only apply when a
new tab is opened.
This PR also adds partial support for the `SGR 6` _rapid blink_
attribute. This is not used by DEC terminals, but was defined in the
ECMA/ANSI standards. It's not widely supported, but many terminals just
it implement it as an alias for the regular `SGR 5` blink attribute, so
that's what I've done here too.
## Validation Steps Performed
I've checked the _Graphic rendition test pattern_ in Vttest, and
compared our representation of the blink attribute to that of an actual
DEC VT220 terminal as seen on [YouTube]. With the right color scheme
it's a reasonably close match.
[YouTube]: https://www.youtube.com/watch?v=03Pz5AmxbE4&t=1m55s
Closes #7388
2020-09-22 01:21:33 +02:00
|
|
|
// Method Description:
|
|
|
|
// - Toggle the blinking rendition state when called by the blink timer.
|
|
|
|
// Arguments:
|
|
|
|
// - sender: not used
|
|
|
|
// - e: not used
|
|
|
|
void TermControl::_BlinkTimerTick(Windows::Foundation::IInspectable const& /* sender */,
|
|
|
|
Windows::Foundation::IInspectable const& /* e */)
|
|
|
|
{
|
|
|
|
if (!_closing)
|
|
|
|
{
|
|
|
|
auto& renderTarget = *_renderer;
|
|
|
|
auto& blinkingState = _terminal->GetBlinkingState();
|
|
|
|
blinkingState.ToggleBlinkingRendition(renderTarget);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Method Description:
|
|
|
|
// - 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 TermControl::_SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition)
|
|
|
|
{
|
2020-05-12 20:23:10 +02:00
|
|
|
if (!_terminal->IsSelectionActive())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
auto terminalPosition = _GetTerminalPosition(cursorPosition);
|
|
|
|
|
|
|
|
const short lastVisibleRow = std::max<short>(_terminal->GetViewport().Height() - 1, 0);
|
|
|
|
const short lastVisibleCol = std::max<short>(_terminal->GetViewport().Width() - 1, 0);
|
|
|
|
|
|
|
|
terminalPosition.Y = std::clamp<short>(terminalPosition.Y, 0, lastVisibleRow);
|
|
|
|
terminalPosition.X = std::clamp<short>(terminalPosition.X, 0, lastVisibleCol);
|
|
|
|
|
|
|
|
// save location (for rendering) + render
|
2020-02-28 01:42:26 +01:00
|
|
|
_terminal->SetSelectionEnd(terminalPosition);
|
2020-02-12 20:06:46 +01:00
|
|
|
_renderer->TriggerSelection();
|
2020-03-25 22:09:49 +01:00
|
|
|
_selectionNeedsToBeCopied = true;
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// Method Description:
|
|
|
|
// - Perform a resize for the current size of the swapchainpanel. If the
|
|
|
|
// font size changed, we'll need to resize the buffer to fit the existing
|
2020-04-30 02:05:29 +02:00
|
|
|
// swapchain size. This helper will call _DoResizeUnderLock with the current size
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// of the swapchain, accounting for scaling due to DPI.
|
|
|
|
// - Note that a DPI change will also trigger a font size change, and will call into here.
|
2020-04-30 02:05:29 +02:00
|
|
|
// - The write lock should be held when calling this method, we might be changing the buffer size in _DoResizeUnderLock.
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
2020-04-30 02:05:29 +02:00
|
|
|
void TermControl::_RefreshSizeUnderLock()
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
{
|
|
|
|
const auto currentScaleX = SwapChainPanel().CompositionScaleX();
|
|
|
|
const auto currentScaleY = SwapChainPanel().CompositionScaleY();
|
|
|
|
const auto actualWidth = SwapChainPanel().ActualWidth();
|
|
|
|
const auto actualHeight = SwapChainPanel().ActualHeight();
|
|
|
|
|
|
|
|
const auto widthInPixels = actualWidth * currentScaleX;
|
|
|
|
const auto heightInPixels = actualHeight * currentScaleY;
|
|
|
|
|
2020-04-30 02:05:29 +02:00
|
|
|
_DoResizeUnderLock(widthInPixels, heightInPixels);
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Method Description:
|
|
|
|
// - Process a resize event that was initiated by the user. This can either
|
|
|
|
// be due to the user resizing the window (causing the swapchain to
|
|
|
|
// resize) or due to the DPI changing (causing us to need to resize the
|
|
|
|
// buffer to match)
|
|
|
|
// Arguments:
|
|
|
|
// - newWidth: the new width of the swapchain, in pixels.
|
|
|
|
// - newHeight: the new height of the swapchain, in pixels.
|
2020-04-30 02:05:29 +02:00
|
|
|
void TermControl::_DoResizeUnderLock(const double newWidth, const double newHeight)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
SIZE size;
|
|
|
|
size.cx = static_cast<long>(newWidth);
|
|
|
|
size.cy = static_cast<long>(newHeight);
|
|
|
|
|
|
|
|
// Don't actually resize so small that a single character wouldn't fit
|
|
|
|
// in either dimension. The buffer really doesn't like being size 0.
|
|
|
|
if (size.cx < _actualFont.GetSize().X || size.cy < _actualFont.GetSize().Y)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-02 21:45:31 +01:00
|
|
|
_terminal->ClearSelection();
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Tell the dx engine that our window is now the new size.
|
|
|
|
THROW_IF_FAILED(_renderEngine->SetWindowSize(size));
|
|
|
|
|
|
|
|
// Invalidate everything
|
|
|
|
_renderer->TriggerRedrawAll();
|
|
|
|
|
|
|
|
// Convert our new dimensions to characters
|
|
|
|
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 },
|
|
|
|
{ static_cast<short>(size.cx), static_cast<short>(size.cy) });
|
|
|
|
const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
|
|
|
|
|
|
|
|
// If this function succeeds with S_FALSE, then the terminal didn't
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// actually change size. No need to notify the connection of this no-op.
|
2020-02-12 20:06:46 +01:00
|
|
|
const HRESULT hr = _terminal->UserResize({ vp.Width(), vp.Height() });
|
|
|
|
if (SUCCEEDED(hr) && hr != S_FALSE)
|
|
|
|
{
|
|
|
|
_connection.Resize(vp.Height(), vp.Width());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-01 03:00:06 +02:00
|
|
|
void TermControl::_TerminalWarningBell()
|
|
|
|
{
|
|
|
|
_WarningBellHandlers(*this, nullptr);
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
void TermControl::_TerminalTitleChanged(const std::wstring_view& wstr)
|
|
|
|
{
|
|
|
|
_titleChangedHandlers(winrt::hstring{ wstr });
|
|
|
|
}
|
2020-08-08 01:07:42 +02:00
|
|
|
void TermControl::_TerminalTabColorChanged(const std::optional<til::color> /*color*/)
|
|
|
|
{
|
|
|
|
_TabColorChangedHandlers(*this, nullptr);
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-06-30 03:55:40 +02:00
|
|
|
void TermControl::_CopyToClipboard(const std::wstring_view& wstr)
|
|
|
|
{
|
Add copyFormatting keybinding arg and array support (#6004)
Adds array support for the existing `copyFormatting` global setting.
This allows users to define which formats they would specifically like
to be copied.
A boolean value is still accepted and is translated to the following:
- `false` --> `"none"` or `[]`
- `true` --> `"all"` or `["html", "rtf"]`
This also adds `copyFormatting` as a keybinding arg for `copy`. As with
the global setting, a boolean value and array value is accepted.
CopyFormat is a WinRT enum where each accepted format is a flag.
Currently accepted formats include `html`, and `rtf`. A boolean value is
accepted and converted. `true` is a conjunction of all the formats.
`false` only includes plain text.
For the global setting, `null` is not accepted. We already have a
default value from before so no worries there.
For the keybinding arg, `null` (the default value) means that we just do
what the global arg says to do. Overall, the `copyFormatting` keybinding
arg is an override of the global setting **when using that keybinding**.
References #5212 - Spec for formatted copying
References #2690 - disable html copy
Validated behavior with every combination of values below:
- `copyFormatting` global: { `true`, `false`, `[]`, `["html"]` }
- `copyFormatting` copy arg:
{ `null`, `true`, `false`, `[]`, `[, "html"]`}
Closes #4191
Closes #5262
2020-08-15 03:02:24 +02:00
|
|
|
auto copyArgs = winrt::make_self<CopyToClipboardEventArgs>(winrt::hstring(wstr));
|
2020-06-30 03:55:40 +02:00
|
|
|
_clipboardCopyHandlers(*this, *copyArgs);
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Method Description:
|
|
|
|
// - Update the position and size of the scrollbar to match the given
|
|
|
|
// viewport top, viewport height, and buffer size.
|
|
|
|
// Additionally fires a ScrollPositionChanged event for anyone who's
|
|
|
|
// registered an event handler for us.
|
|
|
|
// Arguments:
|
|
|
|
// - viewTop: the top of the visible viewport, in rows. 0 indicates the top
|
|
|
|
// of the buffer.
|
|
|
|
// - viewHeight: the height of the viewport in rows.
|
|
|
|
// - bufferSize: the length of the buffer, in rows
|
2020-06-12 21:51:37 +02:00
|
|
|
void TermControl::_TerminalScrollPositionChanged(const int viewTop,
|
|
|
|
const int viewHeight,
|
|
|
|
const int bufferSize)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
// Since this callback fires from non-UI thread, we might be already
|
|
|
|
// closed/closing.
|
|
|
|
if (_closing.load())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-28 21:24:43 +01:00
|
|
|
// Clear the regex pattern tree so the renderer does not try to render them while scrolling
|
|
|
|
_terminal->ClearPatternTree();
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
_scrollPositionChangedHandlers(viewTop, viewHeight, bufferSize);
|
|
|
|
|
2020-06-12 21:51:37 +02:00
|
|
|
ScrollBarUpdate update;
|
|
|
|
const auto hiddenContent = bufferSize - viewHeight;
|
|
|
|
update.newMaximum = hiddenContent;
|
|
|
|
update.newMinimum = 0;
|
|
|
|
update.newViewportSize = viewHeight;
|
|
|
|
update.newValue = viewTop;
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-06-12 21:51:37 +02:00
|
|
|
_updateScrollBar->Run(update);
|
2020-10-28 21:24:43 +01:00
|
|
|
_updatePatternLocations->Run();
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
2020-03-31 01:21:47 +02:00
|
|
|
// Method Description:
|
|
|
|
// - Tells TSFInputControl to redraw the Canvas/TextBlock so it'll update
|
|
|
|
// to be where the current cursor position is.
|
|
|
|
// Arguments:
|
|
|
|
// - N/A
|
2020-06-23 23:05:40 +02:00
|
|
|
void TermControl::_TerminalCursorPositionChanged()
|
2020-03-31 01:21:47 +02:00
|
|
|
{
|
2020-06-23 23:05:40 +02:00
|
|
|
_tsfTryRedrawCanvas->Run();
|
2020-03-31 01:21:47 +02:00
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
hstring TermControl::Title()
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
hstring hstr{ _terminal->GetConsoleTitle() };
|
2020-02-12 20:06:46 +01:00
|
|
|
return hstr;
|
|
|
|
}
|
|
|
|
|
2020-02-28 01:37:56 +01:00
|
|
|
hstring TermControl::GetProfileName() const
|
|
|
|
{
|
|
|
|
return _settings.ProfileName();
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Method Description:
|
|
|
|
// - Given a copy-able selection, get the selected text from the buffer and send it to the
|
|
|
|
// Windows Clipboard (CascadiaWin32:main.cpp).
|
|
|
|
// - CopyOnSelect does NOT clear the selection
|
|
|
|
// Arguments:
|
2020-04-03 01:10:28 +02:00
|
|
|
// - singleLine: collapse all of the text to one line
|
Add copyFormatting keybinding arg and array support (#6004)
Adds array support for the existing `copyFormatting` global setting.
This allows users to define which formats they would specifically like
to be copied.
A boolean value is still accepted and is translated to the following:
- `false` --> `"none"` or `[]`
- `true` --> `"all"` or `["html", "rtf"]`
This also adds `copyFormatting` as a keybinding arg for `copy`. As with
the global setting, a boolean value and array value is accepted.
CopyFormat is a WinRT enum where each accepted format is a flag.
Currently accepted formats include `html`, and `rtf`. A boolean value is
accepted and converted. `true` is a conjunction of all the formats.
`false` only includes plain text.
For the global setting, `null` is not accepted. We already have a
default value from before so no worries there.
For the keybinding arg, `null` (the default value) means that we just do
what the global arg says to do. Overall, the `copyFormatting` keybinding
arg is an override of the global setting **when using that keybinding**.
References #5212 - Spec for formatted copying
References #2690 - disable html copy
Validated behavior with every combination of values below:
- `copyFormatting` global: { `true`, `false`, `[]`, `["html"]` }
- `copyFormatting` copy arg:
{ `null`, `true`, `false`, `[]`, `[, "html"]`}
Closes #4191
Closes #5262
2020-08-15 03:02:24 +02:00
|
|
|
// - formats: which formats to copy (defined by action's CopyFormatting arg). nullptr
|
|
|
|
// if we should defer which formats are copied to the global setting
|
|
|
|
bool TermControl::CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// no selection --> nothing to copy
|
2020-03-27 00:25:11 +01:00
|
|
|
if (!_terminal->IsSelectionActive())
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2020-03-25 22:09:49 +01:00
|
|
|
|
|
|
|
// Mark the current selection as copied
|
|
|
|
_selectionNeedsToBeCopied = false;
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// extract text from buffer
|
2020-04-03 01:10:28 +02:00
|
|
|
const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(singleLine);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// convert text: vector<string> --> string
|
|
|
|
std::wstring textData;
|
|
|
|
for (const auto& text : bufferData.text)
|
|
|
|
{
|
|
|
|
textData += text;
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert text to HTML format
|
2020-04-30 01:41:56 +02:00
|
|
|
// GH#5347 - Don't provide a title for the generated HTML, as many
|
|
|
|
// web applications will paste the title first, followed by the HTML
|
|
|
|
// content, which is unexpected.
|
Add copyFormatting keybinding arg and array support (#6004)
Adds array support for the existing `copyFormatting` global setting.
This allows users to define which formats they would specifically like
to be copied.
A boolean value is still accepted and is translated to the following:
- `false` --> `"none"` or `[]`
- `true` --> `"all"` or `["html", "rtf"]`
This also adds `copyFormatting` as a keybinding arg for `copy`. As with
the global setting, a boolean value and array value is accepted.
CopyFormat is a WinRT enum where each accepted format is a flag.
Currently accepted formats include `html`, and `rtf`. A boolean value is
accepted and converted. `true` is a conjunction of all the formats.
`false` only includes plain text.
For the global setting, `null` is not accepted. We already have a
default value from before so no worries there.
For the keybinding arg, `null` (the default value) means that we just do
what the global arg says to do. Overall, the `copyFormatting` keybinding
arg is an override of the global setting **when using that keybinding**.
References #5212 - Spec for formatted copying
References #2690 - disable html copy
Validated behavior with every combination of values below:
- `copyFormatting` global: { `true`, `false`, `[]`, `["html"]` }
- `copyFormatting` copy arg:
{ `null`, `true`, `false`, `[]`, `[, "html"]`}
Closes #4191
Closes #5262
2020-08-15 03:02:24 +02:00
|
|
|
const auto htmlData = formats == nullptr || WI_IsFlagSet(formats.Value(), CopyFormat::HTML) ?
|
|
|
|
TextBuffer::GenHTML(bufferData,
|
|
|
|
_actualFont.GetUnscaledSize().Y,
|
|
|
|
_actualFont.GetFaceName(),
|
|
|
|
_settings.DefaultBackground()) :
|
|
|
|
"";
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// convert to RTF format
|
Add copyFormatting keybinding arg and array support (#6004)
Adds array support for the existing `copyFormatting` global setting.
This allows users to define which formats they would specifically like
to be copied.
A boolean value is still accepted and is translated to the following:
- `false` --> `"none"` or `[]`
- `true` --> `"all"` or `["html", "rtf"]`
This also adds `copyFormatting` as a keybinding arg for `copy`. As with
the global setting, a boolean value and array value is accepted.
CopyFormat is a WinRT enum where each accepted format is a flag.
Currently accepted formats include `html`, and `rtf`. A boolean value is
accepted and converted. `true` is a conjunction of all the formats.
`false` only includes plain text.
For the global setting, `null` is not accepted. We already have a
default value from before so no worries there.
For the keybinding arg, `null` (the default value) means that we just do
what the global arg says to do. Overall, the `copyFormatting` keybinding
arg is an override of the global setting **when using that keybinding**.
References #5212 - Spec for formatted copying
References #2690 - disable html copy
Validated behavior with every combination of values below:
- `copyFormatting` global: { `true`, `false`, `[]`, `["html"]` }
- `copyFormatting` copy arg:
{ `null`, `true`, `false`, `[]`, `[, "html"]`}
Closes #4191
Closes #5262
2020-08-15 03:02:24 +02:00
|
|
|
const auto rtfData = formats == nullptr || WI_IsFlagSet(formats.Value(), CopyFormat::RTF) ?
|
|
|
|
TextBuffer::GenRTF(bufferData,
|
|
|
|
_actualFont.GetUnscaledSize().Y,
|
|
|
|
_actualFont.GetFaceName(),
|
|
|
|
_settings.DefaultBackground()) :
|
|
|
|
"";
|
2020-02-12 20:06:46 +01:00
|
|
|
|
2020-03-25 22:09:49 +01:00
|
|
|
if (!_settings.CopyOnSelect())
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
_terminal->ClearSelection();
|
2020-02-21 00:03:50 +01:00
|
|
|
_renderer->TriggerSelection();
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// send data up for clipboard
|
|
|
|
auto copyArgs = winrt::make_self<CopyToClipboardEventArgs>(winrt::hstring(textData),
|
|
|
|
winrt::to_hstring(htmlData),
|
Add copyFormatting keybinding arg and array support (#6004)
Adds array support for the existing `copyFormatting` global setting.
This allows users to define which formats they would specifically like
to be copied.
A boolean value is still accepted and is translated to the following:
- `false` --> `"none"` or `[]`
- `true` --> `"all"` or `["html", "rtf"]`
This also adds `copyFormatting` as a keybinding arg for `copy`. As with
the global setting, a boolean value and array value is accepted.
CopyFormat is a WinRT enum where each accepted format is a flag.
Currently accepted formats include `html`, and `rtf`. A boolean value is
accepted and converted. `true` is a conjunction of all the formats.
`false` only includes plain text.
For the global setting, `null` is not accepted. We already have a
default value from before so no worries there.
For the keybinding arg, `null` (the default value) means that we just do
what the global arg says to do. Overall, the `copyFormatting` keybinding
arg is an override of the global setting **when using that keybinding**.
References #5212 - Spec for formatted copying
References #2690 - disable html copy
Validated behavior with every combination of values below:
- `copyFormatting` global: { `true`, `false`, `[]`, `["html"]` }
- `copyFormatting` copy arg:
{ `null`, `true`, `false`, `[]`, `[, "html"]`}
Closes #4191
Closes #5262
2020-08-15 03:02:24 +02:00
|
|
|
winrt::to_hstring(rtfData),
|
|
|
|
formats);
|
2020-02-12 20:06:46 +01:00
|
|
|
_clipboardCopyHandlers(*this, *copyArgs);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Initiate a paste operation.
|
|
|
|
void TermControl::PasteTextFromClipboard()
|
|
|
|
{
|
|
|
|
// attach TermControl::_SendInputToConnection() as the clipboardDataHandler.
|
|
|
|
// This is called when the clipboard data is loaded.
|
|
|
|
auto clipboardDataHandler = std::bind(&TermControl::_SendPastedTextToConnection, this, std::placeholders::_1);
|
|
|
|
auto pasteArgs = winrt::make_self<PasteFromClipboardEventArgs>(clipboardDataHandler);
|
|
|
|
|
|
|
|
// send paste event up to TermApp
|
|
|
|
_clipboardPasteHandlers(*this, *pasteArgs);
|
|
|
|
}
|
|
|
|
|
Don't wait for a connection to finish when a control is closed (#5303)
## Summary of the Pull Request
When a pane is closed by a connection, we want to wait until the connection is actually `Closed` before we fire the actual `Closed` event. If the connection didn't close gracefully, there are scenarios where we want to print a message to the screen.
However, when a pane is closed by the UI, we don't really care to wait for the connection to be completely closed. We can just do it whenever. So I've moved that call to be on a background thread.
## PR Checklist
* [x] Closes #1996
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
Previously we'd wait for the connection to close synchronously when closing tabs or panes. For misbehaving applications like `ssh.exe`, that could result in the `Close` needing to `WaitForSingleObject` _on the UI thread_. If the user closed the tab / pane either with a keybinding or with some other UI element, they don't really care to see the error message anymore. They just want the pane closed. So there's no need to wait for the actual connection to close - the app can just continue on with whatever it was doing.
## Validation Steps Performed
Messed around with closing tabs, panes, tabs with many panes, the entire window. Did this with keybindings, or by clicking on the 'x' on the tab, the 'x' on the window, or using middle-click.
I'm always scared of things like this, so there's a 50% chance this makes things horribly worse.
2020-04-10 01:27:56 +02:00
|
|
|
// Method Description:
|
|
|
|
// - Asynchronously close our connection. The Connection will likely wait
|
|
|
|
// until the attached process terminates before Close returns. If that's
|
|
|
|
// the case, we don't want to block the UI thread waiting on that process
|
|
|
|
// handle.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
winrt::fire_and_forget TermControl::_AsyncCloseConnection()
|
|
|
|
{
|
|
|
|
if (auto localConnection{ std::exchange(_connection, nullptr) })
|
|
|
|
{
|
|
|
|
// Close the connection on the background thread.
|
|
|
|
co_await winrt::resume_background();
|
|
|
|
localConnection.Close();
|
|
|
|
// connection is destroyed.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
void TermControl::Close()
|
|
|
|
{
|
|
|
|
if (!_closing.exchange(true))
|
|
|
|
{
|
|
|
|
// Stop accepting new output and state changes before we disconnect everything.
|
|
|
|
_connection.TerminalOutput(_connectionOutputEventToken);
|
|
|
|
_connectionStateChangedRevoker.revoke();
|
|
|
|
|
2020-02-27 01:35:16 +01:00
|
|
|
TSFInputControl().Close(); // Disconnect the TSF input control so it doesn't receive EditContext events.
|
2020-03-27 00:25:11 +01:00
|
|
|
_autoScrollTimer.Stop();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Don't wait for a connection to finish when a control is closed (#5303)
## Summary of the Pull Request
When a pane is closed by a connection, we want to wait until the connection is actually `Closed` before we fire the actual `Closed` event. If the connection didn't close gracefully, there are scenarios where we want to print a message to the screen.
However, when a pane is closed by the UI, we don't really care to wait for the connection to be completely closed. We can just do it whenever. So I've moved that call to be on a background thread.
## PR Checklist
* [x] Closes #1996
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
Previously we'd wait for the connection to close synchronously when closing tabs or panes. For misbehaving applications like `ssh.exe`, that could result in the `Close` needing to `WaitForSingleObject` _on the UI thread_. If the user closed the tab / pane either with a keybinding or with some other UI element, they don't really care to see the error message anymore. They just want the pane closed. So there's no need to wait for the actual connection to close - the app can just continue on with whatever it was doing.
## Validation Steps Performed
Messed around with closing tabs, panes, tabs with many panes, the entire window. Did this with keybindings, or by clicking on the 'x' on the tab, the 'x' on the window, or using middle-click.
I'm always scared of things like this, so there's a 50% chance this makes things horribly worse.
2020-04-10 01:27:56 +02:00
|
|
|
// GH#1996 - Close the connection asynchronously on a background
|
|
|
|
// thread.
|
|
|
|
// Since TermControl::Close is only ever triggered by the UI, we
|
|
|
|
// don't really care to wait for the connection to be completely
|
|
|
|
// closed. We can just do it whenever.
|
|
|
|
_AsyncCloseConnection();
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
if (auto localRenderEngine{ std::exchange(_renderEngine, nullptr) })
|
|
|
|
{
|
|
|
|
if (auto localRenderer{ std::exchange(_renderer, nullptr) })
|
|
|
|
{
|
|
|
|
localRenderer->TriggerTeardown();
|
|
|
|
// renderer is destroyed
|
|
|
|
}
|
|
|
|
// renderEngine is destroyed
|
|
|
|
}
|
|
|
|
|
2020-03-27 00:25:11 +01:00
|
|
|
// we don't destroy _terminal here; it now has the same lifetime as the
|
|
|
|
// control.
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Scrolls the viewport of the terminal and updates the scroll bar accordingly
|
|
|
|
// Arguments:
|
|
|
|
// - viewTop: the viewTop to scroll to
|
|
|
|
void TermControl::ScrollViewport(int viewTop)
|
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
ScrollBar().Value(viewTop);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int TermControl::GetScrollOffset()
|
|
|
|
{
|
|
|
|
return _terminal->GetScrollOffset();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function Description:
|
|
|
|
// - Gets the height of the terminal in lines of text
|
|
|
|
// Return Value:
|
|
|
|
// - The height of the terminal in lines of text
|
|
|
|
int TermControl::GetViewHeight() const
|
|
|
|
{
|
|
|
|
const auto viewPort = _terminal->GetViewport();
|
|
|
|
return viewPort.Height();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function Description:
|
|
|
|
// - Determines how much space (in pixels) an app would need to reserve to
|
|
|
|
// create a control with the settings stored in the settings param. This
|
|
|
|
// accounts for things like the font size and face, the initialRows and
|
|
|
|
// initialCols, and scrollbar visibility. The returned sized is based upon
|
|
|
|
// the provided DPI value
|
|
|
|
// Arguments:
|
|
|
|
// - settings: A IControlSettings with the settings to get the pixel size of.
|
|
|
|
// - dpi: The DPI we should create the terminal at. This affects things such
|
|
|
|
// as font size, scrollbar and other control scaling, etc. Make sure the
|
|
|
|
// caller knows what monitor the control is about to appear on.
|
|
|
|
// Return Value:
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
// - a size containing the requested dimensions in pixels.
|
|
|
|
winrt::Windows::Foundation::Size TermControl::GetProposedDimensions(IControlSettings const& settings, const uint32_t dpi)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
// If the settings have negative or zero row or column counts, ignore those counts.
|
|
|
|
// (The lower TerminalCore layer also has upper bounds as well, but at this layer
|
|
|
|
// we may eventually impose different ones depending on how many pixels we can address.)
|
|
|
|
const auto cols = ::base::saturated_cast<float>(std::max(settings.InitialCols(), 1));
|
|
|
|
const auto rows = ::base::saturated_cast<float>(std::max(settings.InitialRows(), 1));
|
|
|
|
|
|
|
|
const winrt::Windows::Foundation::Size initialSize{ cols, rows };
|
|
|
|
|
|
|
|
return GetProposedDimensions(initialSize,
|
|
|
|
settings.FontSize(),
|
|
|
|
settings.FontWeight(),
|
|
|
|
settings.FontFace(),
|
|
|
|
settings.ScrollState(),
|
|
|
|
settings.Padding(),
|
|
|
|
dpi);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function Description:
|
|
|
|
// - Determines how much space (in pixels) an app would need to reserve to
|
|
|
|
// create a control with the settings stored in the settings param. This
|
|
|
|
// accounts for things like the font size and face, the initialRows and
|
|
|
|
// initialCols, and scrollbar visibility. The returned sized is based upon
|
|
|
|
// the provided DPI value
|
|
|
|
// Arguments:
|
|
|
|
// - initialSizeInChars: The size to get the proposed dimensions for.
|
|
|
|
// - fontHeight: The font height to use to calculate the proposed size for.
|
|
|
|
// - fontWeight: The font weight to use to calculate the proposed size for.
|
|
|
|
// - fontFace: The font name to use to calculate the proposed size for.
|
|
|
|
// - scrollState: The ScrollbarState to use to calculate the proposed size for.
|
|
|
|
// - padding: The padding to use to calculate the proposed size for.
|
|
|
|
// - dpi: The DPI we should create the terminal at. This affects things such
|
|
|
|
// as font size, scrollbar and other control scaling, etc. Make sure the
|
|
|
|
// caller knows what monitor the control is about to appear on.
|
|
|
|
// Return Value:
|
|
|
|
// - a size containing the requested dimensions in pixels.
|
|
|
|
winrt::Windows::Foundation::Size TermControl::GetProposedDimensions(const winrt::Windows::Foundation::Size& initialSizeInChars,
|
|
|
|
const int32_t& fontHeight,
|
|
|
|
const winrt::Windows::UI::Text::FontWeight& fontWeight,
|
|
|
|
const winrt::hstring& fontFace,
|
2020-08-07 16:46:52 +02:00
|
|
|
const ScrollbarState& scrollState,
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
const winrt::hstring& padding,
|
|
|
|
const uint32_t dpi)
|
|
|
|
{
|
|
|
|
const auto cols = ::base::saturated_cast<int>(initialSizeInChars.Width);
|
|
|
|
const auto rows = ::base::saturated_cast<int>(initialSizeInChars.Height);
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Initialize our font information.
|
|
|
|
// The font width doesn't terribly matter, we'll only be using the
|
|
|
|
// height to look it up
|
|
|
|
// The other params here also largely don't matter.
|
|
|
|
// The family is only used to determine if the font is truetype or
|
|
|
|
// not, but DX doesn't use that info at all.
|
|
|
|
// The Codepage is additionally not actually used by the DX engine at all.
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
FontInfo actualFont = { fontFace, 0, fontWeight.Weight, { 0, gsl::narrow_cast<short>(fontHeight) }, CP_UTF8, false };
|
2020-02-12 20:06:46 +01:00
|
|
|
FontInfoDesired desiredFont = { actualFont };
|
|
|
|
|
|
|
|
// Create a DX engine and initialize it with our font and DPI. We'll
|
|
|
|
// then use it to measure how much space the requested rows and columns
|
|
|
|
// will take up.
|
|
|
|
// TODO: MSFT:21254947 - use a static function to do this instead of
|
|
|
|
// instantiating a DxEngine
|
|
|
|
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
|
|
|
THROW_IF_FAILED(dxEngine->UpdateDpi(dpi));
|
|
|
|
THROW_IF_FAILED(dxEngine->UpdateFont(desiredFont, actualFont));
|
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
const auto scale = dxEngine->GetScaling();
|
2020-02-12 20:06:46 +01:00
|
|
|
const auto fontSize = actualFont.GetSize();
|
|
|
|
|
|
|
|
// UWP XAML scrollbars aren't guaranteed to be the same size as the
|
|
|
|
// ComCtl scrollbars, but it's certainly close enough.
|
|
|
|
const auto scrollbarSize = GetSystemMetricsForDpi(SM_CXVSCROLL, dpi);
|
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
double width = cols * fontSize.X;
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
// Reserve additional space if scrollbar is intended to be visible
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
if (scrollState == ScrollbarState::Visible)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
width += scrollbarSize;
|
|
|
|
}
|
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
double height = rows * fontSize.Y;
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
auto thickness = _ParseThicknessFromPadding(padding);
|
2020-03-23 23:24:33 +01:00
|
|
|
// GH#2061 - make sure to account for the size the padding _will be_ scaled to
|
|
|
|
width += scale * (thickness.Left + thickness.Right);
|
|
|
|
height += scale * (thickness.Top + thickness.Bottom);
|
2020-02-12 20:06:46 +01:00
|
|
|
|
|
|
|
return { gsl::narrow_cast<float>(width), gsl::narrow_cast<float>(height) };
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Get the size of a single character of this control. The size is in
|
|
|
|
// DIPs. If you need it in _pixels_, you'll need to multiply by the
|
|
|
|
// current display scaling.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - The dimensions of a single character of this control, in DIPs
|
|
|
|
winrt::Windows::Foundation::Size TermControl::CharacterDimensions() const
|
|
|
|
{
|
|
|
|
const auto fontSize = _actualFont.GetSize();
|
|
|
|
return { gsl::narrow_cast<float>(fontSize.X), gsl::narrow_cast<float>(fontSize.Y) };
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Get the absolute minimum size that this control can be resized to and
|
|
|
|
// still have 1x1 character visible. This includes the space needed for
|
|
|
|
// the scrollbar and the padding.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - The minimum size that this terminal control can be resized to and still
|
|
|
|
// have a visible character.
|
2020-02-27 01:35:16 +01:00
|
|
|
winrt::Windows::Foundation::Size TermControl::MinimumSize()
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
if (_initializedTerminal)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
const auto fontSize = _actualFont.GetSize();
|
|
|
|
double width = fontSize.X;
|
|
|
|
double height = fontSize.Y;
|
|
|
|
// Reserve additional space if scrollbar is intended to be visible
|
|
|
|
if (_settings.ScrollState() == ScrollbarState::Visible)
|
|
|
|
{
|
|
|
|
width += ScrollBar().ActualWidth();
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
// Account for the size of any padding
|
|
|
|
const auto padding = GetPadding();
|
|
|
|
width += padding.Left + padding.Right;
|
|
|
|
height += padding.Top + padding.Bottom;
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Add support for running a `wt` commandline in the curent window WITH A KEYBINDING (#6537)
## Summary of the Pull Request
Adds a execute commandline action (`wt`), which lets a user bind a key to a specific `wt` commandline. This commandline will get parsed and run _in the current window_.
## References
* Related to #4472
* Related to #5400 - I need this for the commandline mode of the Command Palette
* Related to #5970
## PR Checklist
* [x] Closes oh, there's not actually an issue for this.
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - yes it does
## Detailed Description of the Pull Request / Additional comments
One important part of this change concerns how panes are initialized at runtime. We've had some persistent trouble with initializing multiple panes, because they rely on knowing how big they'll actually be, to be able to determine if they can split again.
We previously worked around this by ignoring the size check when we were in "startup", processing an initial commandline. This PR however requires us to be able to know the initial size of a pane at runtime, but before the parents have necessarily been added to the tree, or had their renderer's set up.
This led to the development of `Pane::PreCalculateCanSplit`, which is very highly similar to `Pane::PreCalculateAutoSplit`. This method attempts to figure out how big a pane _will_ take, before the parent has necessarily laid out.
This also involves a small change to `TermControl`, because if its renderer hasn't been set up yet, it'll always think the font is `{0, fontHeight}`, which will let the Terminal keep splitting in the x direction. This change also makes the TermControl set up a renderer to get the real font size when it hasn't yet been initialized.
## Validation Steps Performed
This was what the json blob I was using for testing evolved into
```json
{
"command": {
"action":"wt",
"commandline": "new-tab cmd.exe /k #work 15 ; split-pane cmd.exe /k #work 15 ; split-pane cmd.exe /k media-commandline ; new-tab powershell dev\\symbols.ps1 ; new-tab -p \"Ubuntu\" ; new-tab -p \"haunter.gif\" ; focus-tab -t 0",
},
"keys": ["ctrl+shift+n"]
}
```
I also added some tests.
# TODO
* [x] Creating a `{ "command": "wt" }` action without a commandline will spawn a new `wt.exe` process?
- Probably should just do nothing for the empty string
2020-07-17 23:05:29 +02:00
|
|
|
return { gsl::narrow_cast<float>(width), gsl::narrow_cast<float>(height) };
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If the terminal hasn't been initialized yet, then the font size will
|
|
|
|
// have dimensions {1, fontSize.Y}, which can mess with consumers of
|
|
|
|
// this method. In that case, we'll need to pre-calculate the font
|
|
|
|
// width, before we actually have a renderer or swapchain.
|
|
|
|
const winrt::Windows::Foundation::Size minSize{ 1, 1 };
|
|
|
|
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
|
|
|
|
const auto dpi = ::base::saturated_cast<uint32_t>(USER_DEFAULT_SCREEN_DPI * scaleFactor);
|
|
|
|
return GetProposedDimensions(minSize,
|
|
|
|
_settings.FontSize(),
|
|
|
|
_settings.FontWeight(),
|
|
|
|
_settings.FontFace(),
|
|
|
|
_settings.ScrollState(),
|
|
|
|
_settings.Padding(),
|
|
|
|
dpi);
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Adjusts given dimension (width or height) so that it aligns to the character grid.
|
|
|
|
// The snap is always downward.
|
|
|
|
// Arguments:
|
|
|
|
// - widthOrHeight: if true operates on width, otherwise on height
|
|
|
|
// - dimension: a dimension (width or height) to be snapped
|
|
|
|
// Return Value:
|
|
|
|
// - A dimension that would be aligned to the character grid.
|
2020-02-27 01:35:16 +01:00
|
|
|
float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
const auto fontSize = _actualFont.GetSize();
|
|
|
|
const auto fontDimension = widthOrHeight ? fontSize.X : fontSize.Y;
|
|
|
|
|
2020-07-08 01:42:42 +02:00
|
|
|
const auto padding = GetPadding();
|
2020-02-12 20:06:46 +01:00
|
|
|
auto nonTerminalArea = gsl::narrow_cast<float>(widthOrHeight ?
|
|
|
|
padding.Left + padding.Right :
|
|
|
|
padding.Top + padding.Bottom);
|
|
|
|
|
|
|
|
if (widthOrHeight && _settings.ScrollState() == ScrollbarState::Visible)
|
|
|
|
{
|
2020-02-27 01:35:16 +01:00
|
|
|
nonTerminalArea += gsl::narrow_cast<float>(ScrollBar().ActualWidth());
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const auto gridSize = dimension - nonTerminalArea;
|
|
|
|
const int cells = static_cast<int>(gridSize / fontDimension);
|
|
|
|
return cells * fontDimension + nonTerminalArea;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Create XAML Thickness object based on padding props provided.
|
|
|
|
// Used for controlling the TermControl XAML Grid container's Padding prop.
|
|
|
|
// Arguments:
|
|
|
|
// - padding: 2D padding values
|
|
|
|
// Single Double value provides uniform padding
|
|
|
|
// Two Double values provide isometric horizontal & vertical padding
|
|
|
|
// Four Double values provide independent padding for 4 sides of the bounding rectangle
|
|
|
|
// Return Value:
|
|
|
|
// - Windows::UI::Xaml::Thickness object
|
|
|
|
Windows::UI::Xaml::Thickness TermControl::_ParseThicknessFromPadding(const hstring padding)
|
|
|
|
{
|
|
|
|
const wchar_t singleCharDelim = L',';
|
|
|
|
std::wstringstream tokenStream(padding.c_str());
|
|
|
|
std::wstring token;
|
|
|
|
uint8_t paddingPropIndex = 0;
|
|
|
|
std::array<double, 4> thicknessArr = {};
|
|
|
|
size_t* idx = nullptr;
|
|
|
|
|
|
|
|
// Get padding values till we run out of delimiter separated values in the stream
|
|
|
|
// or we hit max number of allowable values (= 4) for the bounding rectangle
|
|
|
|
// Non-numeral values detected will default to 0
|
|
|
|
// std::getline will not throw exception unless flags are set on the wstringstream
|
|
|
|
// std::stod will throw invalid_argument exception if the input is an invalid double value
|
|
|
|
// std::stod will throw out_of_range exception if the input value is more than DBL_MAX
|
|
|
|
try
|
|
|
|
{
|
|
|
|
for (; std::getline(tokenStream, token, singleCharDelim) && (paddingPropIndex < thicknessArr.size()); paddingPropIndex++)
|
|
|
|
{
|
|
|
|
// std::stod internally calls wcstod which handles whitespace prefix (which is ignored)
|
|
|
|
// & stops the scan when first char outside the range of radix is encountered
|
|
|
|
// We'll be permissive till the extent that stod function allows us to be by default
|
|
|
|
// Ex. a value like 100.3#535w2 will be read as 100.3, but ;df25 will fail
|
|
|
|
thicknessArr[paddingPropIndex] = std::stod(token, idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
// If something goes wrong, even if due to a single bad padding value, we'll reset the index & return default 0 padding
|
|
|
|
paddingPropIndex = 0;
|
|
|
|
LOG_CAUGHT_EXCEPTION();
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (paddingPropIndex)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
return ThicknessHelper::FromUniformLength(thicknessArr[0]);
|
|
|
|
case 2:
|
|
|
|
return ThicknessHelper::FromLengths(thicknessArr[0], thicknessArr[1], thicknessArr[0], thicknessArr[1]);
|
|
|
|
// No case for paddingPropIndex = 3, since it's not a norm to provide just Left, Top & Right padding values leaving out Bottom
|
|
|
|
case 4:
|
|
|
|
return ThicknessHelper::FromLengths(thicknessArr[0], thicknessArr[1], thicknessArr[2], thicknessArr[3]);
|
|
|
|
default:
|
|
|
|
return Thickness();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Get the modifier keys that are currently pressed. This can be used to
|
|
|
|
// find out which modifiers (ctrl, alt, shift) are pressed in events that
|
|
|
|
// don't necessarily include that state.
|
|
|
|
// Return Value:
|
|
|
|
// - The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
|
|
|
|
ControlKeyStates TermControl::_GetPressedModifierKeys() const
|
|
|
|
{
|
|
|
|
CoreWindow window = CoreWindow::GetForCurrentThread();
|
|
|
|
// DONT USE
|
|
|
|
// != CoreVirtualKeyStates::None
|
|
|
|
// OR
|
|
|
|
// == CoreVirtualKeyStates::Down
|
|
|
|
// Sometimes with the key down, the state is Down | Locked.
|
|
|
|
// Sometimes with the key up, the state is Locked.
|
|
|
|
// IsFlagSet(Down) is the only correct solution.
|
|
|
|
|
|
|
|
struct KeyModifier
|
|
|
|
{
|
|
|
|
VirtualKey vkey;
|
|
|
|
ControlKeyStates flags;
|
|
|
|
};
|
|
|
|
|
|
|
|
constexpr std::array<KeyModifier, 5> modifiers{ {
|
|
|
|
{ VirtualKey::RightMenu, ControlKeyStates::RightAltPressed },
|
|
|
|
{ VirtualKey::LeftMenu, ControlKeyStates::LeftAltPressed },
|
|
|
|
{ VirtualKey::RightControl, ControlKeyStates::RightCtrlPressed },
|
|
|
|
{ VirtualKey::LeftControl, ControlKeyStates::LeftCtrlPressed },
|
|
|
|
{ VirtualKey::Shift, ControlKeyStates::ShiftPressed },
|
|
|
|
} };
|
|
|
|
|
|
|
|
ControlKeyStates flags;
|
|
|
|
|
|
|
|
for (const auto& mod : modifiers)
|
|
|
|
{
|
|
|
|
const auto state = window.GetKeyState(mod.vkey);
|
|
|
|
const auto isDown = WI_IsFlagSet(state, CoreVirtualKeyStates::Down);
|
|
|
|
|
|
|
|
if (isDown)
|
|
|
|
{
|
|
|
|
flags |= mod.flags;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Gets the corresponding viewport terminal position for the cursor
|
|
|
|
// by excluding the padding and normalizing with the font size.
|
|
|
|
// This is used for selection.
|
|
|
|
// Arguments:
|
|
|
|
// - cursorPosition: the (x,y) position of a given cursor (i.e.: mouse cursor).
|
|
|
|
// NOTE: origin (0,0) is top-left.
|
|
|
|
// Return Value:
|
|
|
|
// - the corresponding viewport terminal position for the given Point parameter
|
|
|
|
const COORD TermControl::_GetTerminalPosition(winrt::Windows::Foundation::Point cursorPosition)
|
|
|
|
{
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// cursorPosition is DIPs, relative to SwapChainPanel origin
|
|
|
|
const til::point cursorPosInDIPs{ til::math::rounding, cursorPosition };
|
2020-07-08 01:42:42 +02:00
|
|
|
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// This point is the location of the cursor within the actual grid of characters, in DIPs
|
|
|
|
const til::point relativeToMarginInDIPs = cursorPosInDIPs - marginsInDips;
|
|
|
|
|
|
|
|
// Convert it to pixels
|
|
|
|
const til::point relativeToMarginInPixels{ relativeToMarginInDIPs * SwapChainPanel().CompositionScaleX() };
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// Get the size of the font, which is in pixels
|
|
|
|
const til::size fontSize{ _actualFont.GetSize() };
|
2020-02-12 20:06:46 +01:00
|
|
|
|
Adjusts High DPI scaling to enable differential rendering (#5345)
## Summary of the Pull Request
- Adjusts scaling practices in `DxEngine` (and related scaling practices in `TerminalControl`) for pixel-perfect row baselines and spacing at High DPI such that differential row-by-row rendering can be applied at High DPI.
## References
- #5185
## PR Checklist
* [x] Closes #5320, closes #3515, closes #1064
* [x] I work here.
* [x] Manually tested.
* [x] No doc.
* [x] Am core contributor. Also discussed with some of them already via Teams.
## Detailed Description of the Pull Request / Additional comments
**WAS:**
- We were using implicit DPI scaling on the `ID2D1RenderTarget` and running all of our processing in DIPs (Device-Independent Pixels). That's all well and good for getting things bootstrapped quickly, but it leaves the actual scaling of the draw commands up to the discretion of the rendering target.
- When we don't get to explicitly choose exactly how many pixels tall/wide and our X/Y placement perfectly, the nature of floating point multiplication and division required to do the presentation can cause us to drift off slightly out of our control depending on what the final display resolution actually is.
- Differential drawing cannot work unless we can know the exact integer pixels that need to be copied/moved/preserved/replaced between frames to give to the `IDXGISwapChain1::Present1` method. If things spill into fractional pixels or the sizes of rows/columns vary as they are rounded up and down implicitly, then we cannot do the differential rendering.
**NOW:**
- When deciding on a font, the `DxEngine` will take the scale factor into account and adjust the proposed height of the requested font. Then the remainder of the existing code that adjusts the baseline and integer-ifies each character cell will run naturally from there. That code already works correctly to align the height at normal DPI and scale out the font heights and advances to take an exact integer of pixels.
- `TermControl` has to use the scale now, in some places, and stop scaling in other places. This has to do with how the target's nature used to be implicit and is now explicit. For instance, determining where the cursor click hits must be scaled now. And determining the pixel size of the display canvas must no longer be scaled.
- `DxEngine` will no longer attempt to scale the invalid regions per my attempts in #5185 because the cell size is scaled. So it should work the same as at 96 DPI.
- The block is removed from the `DxEngine` that was causing a full invalidate on every frame at High DPI.
- A TODO was removed from `TermControl` that was invalidating everything when the DPI changed because the underlying renderer will already do that.
## Validation Steps Performed
* [x] Check at 150% DPI. Print text, scroll text down and up, do selection.
* [x] Check at 100% DPI. Print text, scroll text down and up, do selection.
* [x] Span two different DPI monitors and drag between them.
* [x] Giant pile of tests in https://github.com/microsoft/terminal/pull/5345#issuecomment-614127648
Co-authored-by: Dustin Howett <duhowett@microsoft.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
2020-04-22 23:59:51 +02:00
|
|
|
// Convert the location in pixels to characters within the current viewport.
|
|
|
|
return til::point{ relativeToMarginInPixels / fontSize };
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Composition Completion handler for the TSFInputControl that
|
|
|
|
// handles writing text out to TerminalConnection
|
|
|
|
// Arguments:
|
|
|
|
// - text: the text to write to TerminalConnection
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void TermControl::_CompositionCompleted(winrt::hstring text)
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
_connection.WriteInput(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - CurrentCursorPosition handler for the TSFInputControl that
|
|
|
|
// handles returning current cursor position.
|
|
|
|
// Arguments:
|
|
|
|
// - eventArgs: event for storing the current cursor position
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void TermControl::_CurrentCursorPositionHandler(const IInspectable& /*sender*/, const CursorPositionEventArgs& eventArgs)
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
auto lock = _terminal->LockForReading();
|
|
|
|
if (!_initializedTerminal)
|
2020-03-07 04:20:04 +01:00
|
|
|
{
|
Process actions sync. on startup; don't dupe nonexistent profile (#5090)
This PR has evolved to encapsulate two related fixes that I can't really
untie anymore.
#2455 - Duplicating a tab that doesn't exist anymore
This was the bug I was originally fixing in #4429.
When the user tries to `duplicateTab` with a profile that doesn't exist
anymore (like might happen after a settings reload), don't crash.
As I was going about adding tests for this, got blocked by the fact that
the Terminal couldn't open _any_ panes while the `TerminalPage` was size
0x0. This had two theoretical solutions:
* Fake the `TerminalPage` into thinking it had a real size in the test -
probably possible, though I'm unsure how it would work in practice.
* Change `Pane`s to not require an `ActualWidth`, `ActualHeight` on
initialization.
Fortuately, the second option was something else that was already on my
backlog of bugs.
#4618 - `wt` command-line can't consistently parse more than one arg
Presently, the Terminal just arbitrarily dispatches a bunch of handlers
to try and handle all the commands provided on the commandline. That's
lead to a bunch of reports that not all the commands will always get
executed, nor will they all get executed in the same order.
This PR also changes the `TerminalPage` to be able to dispatch all the
commands sequentially, all at once in the startup. No longer will there
be a hot second where the commands seem to execute themselves in from of
the user - they'll all happen behind the scenes on startup.
This involved a couple other changes areound the `TerminalPage`
* I had to make sure that panes could be opened at a 0x0 size. Now they
use a star sizing based off the percentage of the parent they're
supposed to consume, so that when the parent _does_ get laid out,
they'll take the appropriate size of that parent.
* I had to do some math ahead of time to try and calculate what a
`SplitState::Automatic` would be evaluated as, despite the fact that
we don't actually know how big the pane will be.
* I had to ensure that `focus-tab` commands appropriately mark a single
tab as focused while we're in startup, without roundtripping to the
Dispatcher thread and back
## References
#4429 - the original PR for #2455
#5047 - a follow-up task from discussion in #4429
#4953 - a PR for making panes use star sizing, which was immensly
helpful for this PR.
## Detailed Description of the Pull Request / Additional comments
`CascadiaSettings::BuildSettings` can throw if the GUID doesn't exist.
This wraps those calls up with a try/catch.
It also adds a couple tests - a few `SettingsTests` for try/catching
this state. It also adds a XAML-y test in `TabTests` that creates a
`TerminalPage` and then performs som UI-like actions on it. This test
required a minor change to how we generate the new tab dropdown - in the
tests, `Application::Current()` is _not_ a `TerminalApp::App`, so it
doesn't have a `Logic()` to query. So wrap that in a try/catch as well.
While working on these tests, I found that we'd crash pretty agressively
for mysterious reasons if the TestHostApp became focused while the test
was running. This was due to a call in
`TSFInputControl::NotifyFocusEnter` that would callback to
`TSFInputControl::_layoutRequested`, which would crash on setting the
`MaxSize` of the canvas to a negative value. This PR includes a hotfix
for that bug as well.
## Validation Steps Performed
* Manual testing with a _lot_ of commands in a commandline
* run the tests
* Team tested in selfhost
Closes #2455
Closes #4618
2020-03-26 01:03:32 +01:00
|
|
|
// fake it
|
|
|
|
eventArgs.CurrentPosition({ 0, 0 });
|
2020-03-07 04:20:04 +01:00
|
|
|
return;
|
|
|
|
}
|
2020-03-27 00:25:11 +01:00
|
|
|
|
2020-03-31 01:21:47 +02:00
|
|
|
const til::point cursorPos = _terminal->GetCursorPosition();
|
|
|
|
Windows::Foundation::Point p = { ::base::ClampedNumeric<float>(cursorPos.x()), ::base::ClampedNumeric<float>(cursorPos.y()) };
|
2020-02-12 20:06:46 +01:00
|
|
|
eventArgs.CurrentPosition(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - FontInfo handler for the TSFInputControl that
|
|
|
|
// handles returning current font information
|
|
|
|
// Arguments:
|
|
|
|
// - eventArgs: event for storing the current font information
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void TermControl::_FontInfoHandler(const IInspectable& /*sender*/, const FontInfoEventArgs& eventArgs)
|
|
|
|
{
|
|
|
|
eventArgs.FontSize(CharacterDimensions());
|
|
|
|
eventArgs.FontFace(_actualFont.GetFaceName());
|
2020-05-20 22:17:17 +02:00
|
|
|
::winrt::Windows::UI::Text::FontWeight weight;
|
|
|
|
weight.Weight = static_cast<uint16_t>(_actualFont.GetWeight());
|
|
|
|
eventArgs.FontWeight(weight);
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Returns the number of clicks that occurred (double and triple click support)
|
|
|
|
// Arguments:
|
|
|
|
// - clickPos: the (x,y) position of a given cursor (i.e.: mouse cursor).
|
|
|
|
// NOTE: origin (0,0) is top-left.
|
|
|
|
// - clickTime: the timestamp that the click occurred
|
|
|
|
// 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
|
|
|
|
const unsigned int TermControl::_NumberOfClicks(winrt::Windows::Foundation::Point clickPos, Timestamp clickTime)
|
|
|
|
{
|
|
|
|
// if click occurred at a different location or past the multiClickTimer...
|
|
|
|
Timestamp delta;
|
2020-03-10 15:59:16 +01:00
|
|
|
THROW_IF_FAILED(UInt64Sub(clickTime, _lastMouseClickTimestamp, &delta));
|
2020-02-12 20:06:46 +01:00
|
|
|
if (clickPos != _lastMouseClickPos || delta > _multiClickTimer)
|
|
|
|
{
|
|
|
|
// exit early. This is a single click.
|
|
|
|
_multiClickCounter = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_multiClickCounter++;
|
|
|
|
}
|
|
|
|
return _multiClickCounter;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Calculates speed of single axis of auto scrolling. It has to allow for both
|
|
|
|
// fast and precise selection.
|
|
|
|
// Arguments:
|
|
|
|
// - cursorDistanceFromBorder: distance from viewport border to cursor, in pixels. Must be non-negative.
|
|
|
|
// Return Value:
|
|
|
|
// - positive speed in characters / sec
|
|
|
|
double TermControl::_GetAutoScrollSpeed(double cursorDistanceFromBorder) const
|
|
|
|
{
|
|
|
|
// The numbers below just feel well, feel free to change.
|
|
|
|
// TODO: Maybe account for space beyond border that user has available
|
|
|
|
return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Async handler for the "Drop" event. If a file was dropped onto our
|
|
|
|
// root, we'll try to get the path of the file dropped onto us, and write
|
|
|
|
// the full path of the file to our terminal connection. Like conhost, if
|
|
|
|
// the path contains a space, we'll wrap the path in quotes.
|
|
|
|
// - Unlike conhost, if multiple files are dropped onto the terminal, we'll
|
|
|
|
// write all the paths to the terminal, separated by spaces.
|
|
|
|
// Arguments:
|
|
|
|
// - e: The DragEventArgs from the Drop event
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
2020-03-27 00:25:11 +01:00
|
|
|
winrt::fire_and_forget TermControl::_DragDropHandler(Windows::Foundation::IInspectable const& /*sender*/,
|
|
|
|
DragEventArgs const e)
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
if (e.DataView().Contains(StandardDataFormats::StorageItems()))
|
|
|
|
{
|
|
|
|
auto items = co_await e.DataView().GetStorageItemsAsync();
|
|
|
|
if (items.Size() > 0)
|
|
|
|
{
|
|
|
|
std::wstring allPaths;
|
|
|
|
for (auto item : items)
|
|
|
|
{
|
|
|
|
// Join the paths with spaces
|
|
|
|
if (!allPaths.empty())
|
|
|
|
{
|
|
|
|
allPaths += L" ";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::wstring fullPath{ item.Path() };
|
|
|
|
const auto containsSpaces = std::find(fullPath.begin(),
|
|
|
|
fullPath.end(),
|
|
|
|
L' ') != fullPath.end();
|
|
|
|
|
|
|
|
auto lock = _terminal->LockForWriting();
|
|
|
|
|
|
|
|
if (containsSpaces)
|
|
|
|
{
|
|
|
|
fullPath.insert(0, L"\"");
|
|
|
|
fullPath += L"\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
allPaths += fullPath;
|
|
|
|
}
|
|
|
|
_SendInputToConnection(allPaths);
|
|
|
|
}
|
|
|
|
}
|
2020-05-12 23:46:47 +02:00
|
|
|
else if (e.DataView().Contains(StandardDataFormats::Text()))
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
std::wstring text{ co_await e.DataView().GetTextAsync() };
|
|
|
|
_SendPastedTextToConnection(text);
|
|
|
|
}
|
|
|
|
CATCH_LOG();
|
|
|
|
}
|
2020-02-12 20:06:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Handle the DragOver event. We'll signal that the drag operation we
|
|
|
|
// support is the "copy" operation, and we'll also customize the
|
|
|
|
// appearance of the drag-drop UI, by removing the preview and setting a
|
|
|
|
// custom caption. For more information, see
|
|
|
|
// https://docs.microsoft.com/en-us/windows/uwp/design/input/drag-and-drop#customize-the-ui
|
|
|
|
// Arguments:
|
|
|
|
// - e: The DragEventArgs from the DragOver event
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void TermControl::_DragOverHandler(Windows::Foundation::IInspectable const& /*sender*/,
|
|
|
|
DragEventArgs const& e)
|
|
|
|
{
|
2020-03-27 00:25:11 +01:00
|
|
|
if (_closing)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-05-12 23:46:47 +02:00
|
|
|
// We can only handle drag/dropping StorageItems (files) and plain Text
|
|
|
|
// currently. If the format on the clipboard is anything else, returning
|
|
|
|
// early here will prevent the drag/drop from doing anything.
|
|
|
|
if (!(e.DataView().Contains(StandardDataFormats::StorageItems()) ||
|
|
|
|
e.DataView().Contains(StandardDataFormats::Text())))
|
2020-02-12 20:06:46 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure to set the AcceptedOperation, so that we can later receive the path in the Drop event
|
|
|
|
e.AcceptedOperation(DataPackageOperation::Copy);
|
|
|
|
|
|
|
|
// Sets custom UI text
|
2020-05-12 23:46:47 +02:00
|
|
|
if (e.DataView().Contains(StandardDataFormats::StorageItems()))
|
|
|
|
{
|
|
|
|
e.DragUIOverride().Caption(RS_(L"DragFileCaption"));
|
|
|
|
}
|
|
|
|
else if (e.DataView().Contains(StandardDataFormats::Text()))
|
|
|
|
{
|
|
|
|
e.DragUIOverride().Caption(RS_(L"DragTextCaption"));
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// Sets if the caption is visible
|
|
|
|
e.DragUIOverride().IsCaptionVisible(true);
|
|
|
|
// Sets if the dragged content is visible
|
|
|
|
e.DragUIOverride().IsContentVisible(false);
|
ci: run spell check in CI, fix remaining issues (#4799)
This commit introduces a github action to check our spelling and fixes
the following misspelled words so that we come up green.
It also renames TfEditSes to TfEditSession, because Ses is not a word.
currently, excerpt, fallthrough, identified, occurred, propagate,
provided, rendered, resetting, separate, succeeded, successfully,
terminal, transferred, adheres, breaks, combining, preceded,
architecture, populated, previous, setter, visible, window, within,
appxmanifest, hyphen, control, offset, powerpoint, suppress, parsing,
prioritized, aforementioned, check in, build, filling, indices, layout,
mapping, trying, scroll, terabyte, vetoes, viewport, whose
2020-03-25 19:02:53 +01:00
|
|
|
// Sets if the glyph is visible
|
2020-02-12 20:06:46 +01:00
|
|
|
e.DragUIOverride().IsGlyphVisible(false);
|
|
|
|
}
|
|
|
|
|
2020-09-03 19:52:39 +02:00
|
|
|
// Method description:
|
|
|
|
// - Checks if the uri is valid and sends an event if so
|
|
|
|
// Arguments:
|
|
|
|
// - The uri
|
|
|
|
void TermControl::_HyperlinkHandler(const std::wstring_view uri)
|
|
|
|
{
|
|
|
|
auto hyperlinkArgs = winrt::make_self<OpenHyperlinkEventArgs>(winrt::hstring{ uri });
|
|
|
|
_openHyperlinkHandlers(*this, *hyperlinkArgs);
|
|
|
|
}
|
|
|
|
|
2020-04-14 22:11:47 +02:00
|
|
|
// Method Description:
|
|
|
|
// - Produces the error dialog that notifies the user that rendering cannot proceed.
|
|
|
|
winrt::fire_and_forget TermControl::_RendererEnteredErrorState()
|
|
|
|
{
|
|
|
|
auto strongThis{ get_strong() };
|
|
|
|
co_await Dispatcher(); // pop up onto the UI thread
|
|
|
|
|
|
|
|
if (auto loadedUiElement{ FindName(L"RendererFailedNotice") })
|
|
|
|
{
|
|
|
|
if (auto uiElement{ loadedUiElement.try_as<::winrt::Windows::UI::Xaml::UIElement>() })
|
|
|
|
{
|
|
|
|
uiElement.Visibility(Visibility::Visible);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Responds to the Click event on the button that will re-enable the renderer.
|
|
|
|
void TermControl::_RenderRetryButton_Click(IInspectable const& /*sender*/, IInspectable const& /*args*/)
|
|
|
|
{
|
|
|
|
// It's already loaded if we get here, so just hide it.
|
|
|
|
RendererFailedNotice().Visibility(Visibility::Collapsed);
|
|
|
|
_renderer->ResetErrorStateAndResume();
|
|
|
|
}
|
|
|
|
|
2020-08-10 18:21:56 +02:00
|
|
|
IControlSettings TermControl::Settings() const
|
|
|
|
{
|
|
|
|
return _settings;
|
|
|
|
}
|
|
|
|
|
2020-08-08 01:07:42 +02:00
|
|
|
Windows::Foundation::IReference<winrt::Windows::UI::Color> TermControl::TabColor() noexcept
|
|
|
|
{
|
|
|
|
auto coreColor = _terminal->GetTabColor();
|
|
|
|
return coreColor.has_value() ? Windows::Foundation::IReference<winrt::Windows::UI::Color>(coreColor.value()) : nullptr;
|
|
|
|
}
|
|
|
|
|
2020-02-12 20:06:46 +01:00
|
|
|
// -------------------------------- WinRT Events ---------------------------------
|
|
|
|
// Winrt events need a method for adding a callback to the event and removing the callback.
|
|
|
|
// These macros will define them both for you.
|
|
|
|
DEFINE_EVENT(TermControl, TitleChanged, _titleChangedHandlers, TerminalControl::TitleChangedEventArgs);
|
|
|
|
DEFINE_EVENT(TermControl, FontSizeChanged, _fontSizeChangedHandlers, TerminalControl::FontSizeChangedEventArgs);
|
|
|
|
DEFINE_EVENT(TermControl, ScrollPositionChanged, _scrollPositionChangedHandlers, TerminalControl::ScrollPositionChangedEventArgs);
|
|
|
|
|
|
|
|
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, PasteFromClipboard, _clipboardPasteHandlers, TerminalControl::TermControl, TerminalControl::PasteFromClipboardEventArgs);
|
|
|
|
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, CopyToClipboard, _clipboardCopyHandlers, TerminalControl::TermControl, TerminalControl::CopyToClipboardEventArgs);
|
2020-09-03 19:52:39 +02:00
|
|
|
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, OpenHyperlink, _openHyperlinkHandlers, TerminalControl::TermControl, TerminalControl::OpenHyperlinkEventArgs);
|
2020-02-12 20:06:46 +01:00
|
|
|
// clang-format on
|
|
|
|
}
|