Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split TermControl into a Core, Interactivity, and Control layer #9820

Merged
98 commits merged into from
Apr 27, 2021

Conversation

zadjii-msft
Copy link
Member

@zadjii-msft zadjii-msft commented Apr 14, 2021

Summary of the Pull Request

Brace yourselves, it's finally here. This PR does the dirty work of splitting the monolithic TermControl into three components. These components are:

  • ControlCore: This encapsulates the Terminal instance, the DxEngine and Renderer, and the Connection. This is intended to everything that someone might need to stand up a terminal instance in a control, but without any regard for how the UX works.
  • ControlInteractivity: This is a wrapper for the ControlCore, which holds the logic for things like double-click, right click copy/paste, selection, etc. This is intended to be a UI framework-independent abstraction. The methods this layer exposes can be called the same from both the WinUI TermControl and the WPF control.
  • TermControl: This is the UWP control. It's got a Core and Interactivity inside it, which it uses for the actual logic of the terminal itself. TermControl's main responsibility is now

By splitting into smaller pieces, it will enable us to

  • write unit tests for the Core and Interactivity bits, which we desparately need
  • Combine ControlCore and ControlInteractivity in an out-of-proc core process in the future, to enable tab tearout.

However, we're not doing that work quite yet. There's still lots of work to be done to enable that, thought this is likely the biggest portion.

Ideally, this would just be methods moved wholesale from one file to another. Unfortunately, there are a bunch of cases where that didn't work as well as expected. Especially when trying to better enforce the boundary between the classes.

We've got a couple tests here that I've added. These are partially examples, and partially things I ran into while implementing this. A bunch of things from #7001 can go in now that we have this.

This PR is gonna be a huge pain to review - 38 files with 3,730 additions and 1,661 deletions is nothing to scoff at. It will also conflict 100% with anything that's targeting TermControl. I'm hoping we can review this over the course of the next week and just be done with it, and leave plenty of runway for 1.9 bugs in post.

References

PR Checklist

Detailed Description of the Pull Request / Additional comments

  • I don't love the names ControlCore and ControlInteractivity. Open to other names.
  • I added a ICoreState interface for "properties that come from the ControlCore, but consumers of the TermControl need to know". In the future, these will all need to be handled specially, because they might involve an RPC call to retrieve the info from the core (or cache it) in the window process.
  • I've added more EventArgs to make more events proper TypedEvents.
  • I've changed how the TerminalApp layer requests updated TaskbarProgress state. It doesn't need to pump TermControl to raise a new event anymore.
  • Something that snuck into this branch in the very long history is the switch to DCompositionCreateSurfaceHandle for the DxEngine. @miniksa wrote this originally in 30b8335, I'm just finally committing it here. We'll need that in the future for the out-of-proc stuff.
    • I reverted this in c113b65. We can revert that commit when we want to come back to it.
  • I've changed the acrylic handler a decent amount. But added tests!
  • All the ThrottledFunc things are left in TermControl. Some might be able to move down into core/interactivity, but once we figure out how to use a different kind of Dispatcher (because a UI thread won't necessarily exist for those components).
  • I've undoubtably messed up the merging of the locking around the appearance config stuff recently

Validation Steps Performed

I've got a rolling list in #6842 (comment) that I'm updating as I go.

  This is the code as of 4703dfe

  I'm moving it to TermControl in a new branch, to actually try checking it out now that I think it's mostly done.

  There are still 14 !TODO!s but this should at least let me start testing
  Wadda ya know, we _can_ have TerminalCore expose types. Maybe we just got that in a recent VS / cppwinrt update, but it works finally
…rminalcontrol-into-lib

# Conflicts:
#	src/cascadia/TerminalApp/TerminalSettings.h
#	src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
…le. Convert some older macro uses to TYPED_EVENT
…l' into dev/migrie/oop-terminal.control-split-control
…al.control-split-control

# Conflicts:
#	src/cascadia/TerminalApp/TerminalPage.cpp
#	src/cascadia/TerminalApp/TerminalPage.h
#	src/cascadia/TerminalControl/TermControl.cpp
#	src/cascadia/TerminalControl/TermControl.h
#	src/cascadia/TerminalControl/TermControl.idl
#	src/cascadia/TerminalControl/TerminalControl.def
#	src/cascadia/TerminalControl/TerminalControlLib.vcxproj
#	src/cascadia/TerminalControl/dll/Microsoft.Terminal.Control.def
#	src/cascadia/TerminalControl/dll/TerminalControl.def
#	src/cascadia/TerminalControl/dll/TerminalControl.vcxproj
…nteractivity-control

  UpdateHoveredCell was a MESS
  This also moved _HyperlinkHandler, and _TrySendMouseEvent

  Started with 29 errors, down to 24
  Now we're down to 12 errors. This is starting to feel like it's coming
  together. Mouse scrolling is scary though.
  And a bit of cleanup of things left behind by the previous commits.
  Down to 5 errors!
Copy link
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sold. You addressed my 150 comments. Thank you for doing that, and I am excited to re-platform the WPF control on top of this.

Unresolved comments:

You could also write the following:

hostingTab.TaskbarProgressChanged({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });

Unfortunately there's no explanation in this file why get_weak() is being used in certain places instead of a simple this. If you know why a comment would probably be useful and prevent someone from breaking it in the future.

@lhecker https://github.com/microsoft/terminal/pull/9820/files#r616981656

If you make this method resistant against being called from a background thread you don't need to switch into the foreground thread in neither _RendererWarning nor _RaiseReadOnlyWarning.

@lhecker #9820 (comment)

@DHowett
Copy link
Member

DHowett commented Apr 22, 2021

@carlos-zamora you are a waiter on this PR.

@miniksa You and I discussed this, so I may dismiss your review.

@zadjii-msft zadjii-msft mentioned this pull request Apr 26, 2021
5 tasks
Copy link
Member

@carlos-zamora carlos-zamora left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, let's do this.

@zadjii-msft zadjii-msft dismissed miniksa’s stale review April 27, 2021 15:50

Dustin said it was cool if I dismiss you, so 👋

@zadjii-msft zadjii-msft added the AutoMerge Marked for automatic merge by the bot when requirements are met label Apr 27, 2021
@ghost
Copy link

ghost commented Apr 27, 2021

Hello @zadjii-msft!

Because this pull request has the AutoMerge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit 8910a16 into main Apr 27, 2021
@ghost ghost deleted the dev/migrie/oop-core-interactivity-control branch April 27, 2021 15:50
@zadjii-msft zadjii-msft mentioned this pull request Apr 28, 2021
3 tasks
ghost pushed a commit that referenced this pull request May 4, 2021
## Summary of the Pull Request

This PR encompasses the first three bugs we found post-#9820.

### A: Mousedown, select, SCROLL does a weird thing with endpoints that doesn't happen in stable

We were using the terminal position to set the selection anchor, when we should have used the pixel position.

This is fixed in 4f4df01.

### B: Trackpad scrolling down with small increments seems buggy

This one's the most complicated.  The touchpad sends very many small scroll deltas, less than one row at a time. The control scrollbar can store a `double`, so small deltas can accumulate. Originally, these would accumulate in the scrollbar, and we'd only read that out as an `int` in the scrollbar updater, which is throttled. 

In the interactivity split, there's no place for us to store that double. We immediately narrow to an `int` for `ControlInteractivity::_updateScrollbar`. 

So this introduces a double inside `ControlInteractivity` as a fake scrollbar, with which to accumulate to. 

This is fixed in 33d29fa...0fefc5b

### C:  Looks like there's a selection issue when you click and drag too quickly.

The diff for this one is:

<table>

<tr><td>1.8</td><td>main</td></tr>
<tr>

<td>

```c++
if (_singleClickTouchdownPos)
{
    // Figure out if the user's moved a quarter of a cell's smaller axis away from the clickdown point
    auto& touchdownPoint{ *_singleClickTouchdownPos };
    auto distance{ std::sqrtf(std::powf(cursorPosition.X - touchdownPoint.X, 2) + std::powf(cursorPosition.Y - touchdownPoint.Y, 2)) };
    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))
    {
        _terminal->SetSelectionAnchor(_GetTerminalPosition(touchdownPoint));
        // stop tracking the touchdown point
        _singleClickTouchdownPos = std::nullopt;
    }
}
```

</td>

<td>

```c++
if (_singleClickTouchdownPos)
{
    // Figure out if the user's moved a quarter of a cell's smaller axis away from the clickdown point
    auto& touchdownPoint{ *_singleClickTouchdownPos };
    float dx = ::base::saturated_cast<float>(pixelPosition.x() - touchdownPoint.x());
    float dy = ::base::saturated_cast<float>(pixelPosition.y() - touchdownPoint.y());
    auto distance{ std::sqrtf(std::powf(dx, 2) +
                              std::powf(dy, 2)) };

    const auto fontSizeInDips{ _core->FontSizeInDips() };
    if (distance >= (std::min(fontSizeInDips.width(), fontSizeInDips.height()) / 4.f))
    {
        _core->SetSelectionAnchor(terminalPosition);
        // stop tracking the touchdown point
        _singleClickTouchdownPos = std::nullopt;
    }
}
```

</td>
</tr>
</table>

```c++
        _terminal->SetSelectionAnchor(_GetTerminalPosition(touchdownPoint));
```

vs

```c++
        _core->SetSelectionAnchor(terminalPosition);
```

We're now using the location of the drag event as the selection anchor, instead of the location that the user initially clicked. Oops.


## PR Checklist
* [x] Checks three boxes, though I'll be shocked if they're the last.
* [x] I work here
* [x] Tests added/passed 🎉🎉🎉
* [n/a] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

All three have tests, 🙌🙌🙌🙌

## Validation Steps Performed

Manual, and automated via tests
@ghost
Copy link

ghost commented May 25, 2021

🎉Windows Terminal Preview v1.9.1445.0 has been released which incorporates this pull request.:tada:

Handy links:

ghost pushed a commit that referenced this pull request Aug 11, 2021
## Summary of the Pull Request

This was missed in #10051. We need to make sure that the UIA provider can immediately know about the padding in the control, not just after the settings reload.

## PR Checklist
* [x] Closes #9955.e
  * [x] Additionally, this just closes #9955. The only remaining box in there never repro'd, so probably wasn't even root caused by #9820. I think we can close that issue for now, and reactivate if something else was broken.
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated

## Validation Steps Performed

Checked before/after in Accessibility Insights. Before the row rectangles were the full width of the control initially. Now they're properly padded.
ghost pushed a commit that referenced this pull request Aug 26, 2021
This is on me. When I got rid of the `_updatePatternLocations` `ThrottledFunc` in the `TermControl`, I didn't add a matching call to `_updatePatternLocations->Run()` in this method.

In #9820, in `TermControl::_ScrollPositionChanged`, there was still a call to `_updatePatternLocations->Run();`. (TermControl.cpp:1655 on the right) https://github.com/microsoft/terminal/pull/9820/files#diff-c10bb023995e88dac6c1d786129284c454c2df739ea547ce462129dc86dc2697R1654

#10051 didn't change this

In #10187 I moved the `_updatePatternLocations` throttled func from termcontrol to controlcore. Places it existed before:
* [x] `TermControl::_coreReceivedOutput`: already matched by ControlCore::_connectionOutputHandler
* [x] `TermControl::_ScrollbarChangeHandler` -> added in c20eb9d
* [x] `TermControl::_ScrollPositionChanged` -> `ControlCore::_terminalScrollPositionChanged`

## Validation Steps Performed
Print a URL, scroll the wheel: it still works.

Closes #11055
DHowett pushed a commit that referenced this pull request Aug 26, 2021
This is on me. When I got rid of the `_updatePatternLocations` `ThrottledFunc` in the `TermControl`, I didn't add a matching call to `_updatePatternLocations->Run()` in this method.

In #9820, in `TermControl::_ScrollPositionChanged`, there was still a call to `_updatePatternLocations->Run();`. (TermControl.cpp:1655 on the right) https://github.com/microsoft/terminal/pull/9820/files#diff-c10bb023995e88dac6c1d786129284c454c2df739ea547ce462129dc86dc2697R1654

#10051 didn't change this

In #10187 I moved the `_updatePatternLocations` throttled func from termcontrol to controlcore. Places it existed before:
* [x] `TermControl::_coreReceivedOutput`: already matched by ControlCore::_connectionOutputHandler
* [x] `TermControl::_ScrollbarChangeHandler` -> added in c20eb9d
* [x] `TermControl::_ScrollPositionChanged` -> `ControlCore::_terminalScrollPositionChanged`

## Validation Steps Performed
Print a URL, scroll the wheel: it still works.

Closes #11055

(cherry picked from commit 7423734)
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-CodeHealth Issues related to code cleanliness, linting, rules, warnings, errors, static analysis, etc. Area-TerminalControl Issues pertaining to the terminal control (input, selection, keybindings, mouse interaction, etc.) Area-WPFControl Things related to the WPF version of the TermControl AutoMerge Marked for automatic merge by the bot when requirements are met Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. Product-Terminal The new Windows Terminal.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Introduce abstraction layer between TermControl and TerminalCore (aka Interactivity)
6 participants