-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Snap to character grid when resizing window #3181
Snap to character grid when resizing window #3181
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments throughout. I'll take a second and more in-depth pass of Pane.cpp later.
If possible, I'd like @zadjii-msft to sign off on this when he gets back
(I am very excited about this and just need some time to give it a review.) |
9cb587c
to
f12cc5e
Compare
f12cc5e
to
b00fef8
Compare
Nice! Definitely needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I owe this a so much longer review, but I wanted to get these thoughts in before the weekend:
- Amazing work here
- I'm definitely concerned about the _root thing I called out below
- Is this disable-able by any chance? If someone wants smooth resizing like it is currently, is that something that could still be enabled with a setting? (not saying I'll block on this if it isn't, I'm just curious)
- Heads up, I merged Indicate which pane is focused with the Accent color on the pan… #3060, so there's gonna be some conflicts with that 😧 I'm so sorry
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So first off, this is definitely a change I want to see land. Resizing seems much more stable with it, and the snapping is really slick. That being said, I still have very little idea how it works. I think in _AdvanceSnappedDimension
especially, additional comments would help clean things up, as well as adding more types just to help clear up the differences between the many different usages of std::pair<float, float>
throughout the code.
I'm also very worried about the runtime of this calculation. Seems like it repeats a lot of work, though I'm not totally sure how at this time to avoid that step. Let's iterate on this a bit, but I'm hopeful that we can land this change. Excellent work overall 😀
float TerminalPage::SnapDimension(const bool widthOrHeight, const float dimension) const | ||
{ | ||
const auto focusedTabIndex = _GetFocusedTabIndex(); | ||
return _tabs[focusedTabIndex]->SnapDimension(widthOrHeight, dimension); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self: This is probably where I'd implement a setting to disable the snapping. By simply not calling SnamDimension here, and just returning dimension
, then the AppHost layer will act as though the proposed snap size was okay, and it'll still smooth resize.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than having method named SnapDimension
that sometimes doesn't do its job, it might be cleaner just not to call it at all in IslandWindow
.
Secondly, that would only disable the 'window' snapping but not 'pane' snapping (so if one splits terminal vertically and changes the window's width, the left pane will still snap, although the whole window would go smoothly). Should it be this way? Maybe make this 3-way setting that would also cover that (like fullSnap
, innerSnap
, noSnap
).
// - dimension: a dimension (width or height) to be snapped | ||
// Return Value: | ||
// - A dimension that would be aligned to the character grid. | ||
float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension) const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading this method, I'd maybe want this first param named something like
float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension) const | |
float TermControl::SnapDimensionToGrid(const bool snapToWidth, const float dimension) const |
instead, especially lower:
if (snapToWidth && _settings.ScrollState() == ScrollbarState::Visible)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If so, this should be renamed everywhere (so in a lot of places). I went with widthOrHeight
because I find it more readable. With some thing like isWidth
, it is not so apparent what it means if it's false. Usually not-width means height, but not always.
About performance: Form my measurements it appears that the introduced layouting code takes usually about 1% CPU time of whole resize operation (and the thing to note is that it runs on every mouse move event, while the actual terminal resizes runs only at the 'snaps`). On the other hand the terminal resize takes way to long. I've tried to make this O(1) instead of the current O(n) (where n is number of cells fitting given dimension) but failed to do so As of caching: the XAML prop reads really did slow down the algorithm by few times, even on release config. But since I measured it the algorithm changed quite drastically and now these properties are being read many less times (in favor of character size, which does not go through xaml) + I just discovered I can cache the padding locally instead of reading it for each edge. This makes it far less buttlenecking and in fact rather irrelevant so I can safely undo the cacheing. |
@zadjii-msft Ok, screw it. The problem is that since panes now have borders inside them and some panes have more borders than others (because e.g. some are on the edge of the window), thus some are bigger than others, although terminals are the same. This somewhat breaks the nice order of growth that was established earlier, because now panes that are in the middle are favored, since they are smaller. But that's not terribly bad, if noticeable, so I won't block on that. Fixing this would require differentiating between content and border size and that seems a little bit hard and messy. I might take a follow up on this. Another problem I noticed is that |
|
@miniksa re: point 1 - I'm fully prepared with a PR to make this a disable-able setting once this merges 😄 |
I can't get it built ATM for some reason but I'm pretty sure it fullscreening worked well. It's only on WM_SIZING that something happens, so only when user drags an edge of window. Regarding the disabilisness, as I mentioned somewhere, apart from the on and off states, there could be an interesting option to only snap panes, but not window. Can there be a setting that is both boolean and enum? (Typescript can handle that). If not it could be named something like Edit: Yup, fullscrean works. |
Yea that's how I planned on implementing it - we can differentiate between a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yea I'm much happier with that name. Thanks for this 😄
@mcpiroman, I know I said in your other PR 4068 that I'd try to get to this one today because I duplicate commented everywhere. But I got tied up in other PRs this morning and early afternoon and now I have to go for the day. (I was overdue on a few for @j4james...) This is now on the top of my list for Thursday morning after the New Year holiday. Thanks for your patience. |
@miniksa No worries, I wouldn't look at that anyway - it's almost new year already at my timezone |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went through this a bit. I don't see anything major left except other people's comment threads that still aren't marked as resolved or closed. I didn't have much else to add. I'm going to not hit the second checkmark, though, because I feel like @DHowett-MSFT had a deeper understanding of this than me and I want to leave him the chance to be second sign off before merge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I love this. I'm going to check it out, merge it, play with it, and then merge the PR if it feels right. 😄
const auto snapPossibilites = _CalcSnappedDimension(widthOrHeight, dimension); | ||
const auto lower = snapPossibilites.lower; | ||
const auto higher = snapPossibilites.higher; | ||
return dimension - lower < higher - dimension ? lower : higher; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fine. I was thinking that we could use `((dimension - lower) < (higher - dimension)) ? lower : higher. I'm okay with this though.
@mcpiroman thanks for your contribution 😄 |
🎉 Handy links: |
I originally just wanted to close #1104, but then discovered that hey, this event wasn't even used anymore. Excerpts of Teams convo: * [Snap to character grid when resizing window by mcpiroman · Pull Request #3181 · microsoft/terminal (github.com)](https://github.com/microsoft/terminal/pull/3181/files#diff-d7ca72e0d5652fee837c06532efa614191bd5c41b18aa4d3ee6711f40138f04c) added it to Tab.cpp * where it was added * which called `pane->Relayout` which I don't even REMEMBER * By [Add functionality to open the Settings UI tab through openSettings by leonMSFT · Pull Request #7802 · microsoft/terminal (github.com)](https://github.com/microsoft/terminal/pull/7802/files#diff-83d260047bed34d3d9d5a12ac62008b65bd6dc5f3b9642905a007c3efce27efd), there was seemingly no FontSizeChanged in Tab.cpp (when it got moved to terminaltab.cpp) > `Pane::Relayout` functionally did nothing because sizing was switched to `star` sizing at some point in the past, so it was just deleted. From [Misc pane refactoring by Rosefield · Pull Request #11373 · microsoft/terminal](https://github.com/microsoft/terminal/pull/11373/files#r736900998) So, great. We can kill part of it, and convert the rest to a `TypedEvent`, and get rid of `DECLARE_` / `DEFINE_`. `ScrollPositionChangedEventArgs` was ALSO apparently already promoted to a typed event, so kill that too.
When user resizes window, snap the size to align with the character grid (like e.g. putty, mintty and most unix terminals). Properly resolves arbitrary pane configuration (even with different font sizes and padding) trying to align each pane as close as possible.
It also fixes terminal minimum size enforcement which was not quite well handled, especially with multiple panes.
This PR does not however try to keep the terminals aligned at other user actions (e.g. font change or pane split). That is to be tracked by some other activity.
References
#3060 introduces some differences in size calculations by moving from separator to borders, but it should be easy to adopt to that.
PR Checklist
Detailed Description of the Pull Request / Additional comments
Snapping is resolved in the pane tree, recursively, so it (hopefully) works for any possible layout.
Along the way I had to clean up some things as so to make the resulting code not so cumbersome:
Pane.cpp
: Replaced_firstPercent
and_secondPercent
with single_desiredSplitPosition
to reduce invariants - these had to be kept in sync so their sum always gives 1 (and were not really a percent). Thedesired
part refers to fact that since panes are aligned, there is usually some deviation from that ratio.Pane.cpp
: Fixed_GetMinSize()
- it was improperly accounting for split directionTerminalControl
: Made dedicated member for padding instead of reading it from a control itself. This is because the winrt property functions turned out to be slow and this algorithm needs to access it many times. I also cached scrollbar width for the same reason.AppHost
: Moved window to client size resolution to virtual method, whereIslandWinow
andNonClientIslandWindow
have their own implementations (as opposite to pointer casting).One problem with current implementation is I had to make a long call chain from the window that requests snapping to the (root) pane that implements it:
IslandWindow
->AppHost
's callback ->App
->TerminalPage
->Tab
->Pane
Idk if this can be done better.
Validation Steps Performed
Spam split pane buttons, randomly change font sizes with ctrl+mouse wheel and drag the window back and forth.