Skip to content

Commit

Permalink
revising a bit in light of the 6 months of not touching this
Browse files Browse the repository at this point in the history
  • Loading branch information
leonMSFT committed Jun 15, 2020
1 parent ce19d01 commit fda6a36
Showing 1 changed file with 11 additions and 14 deletions.
25 changes: 11 additions & 14 deletions doc/specs/#1502 - Advanced Tab Switcher/spec.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
author: Leon Liang @leonMSFT
created on: 2019-11-27
last updated: 2019-12-03
last updated: 2019-06-15
issue id: 1502
---

Expand All @@ -27,27 +27,25 @@ Visual Studio's tab switcher presents itself as a box in the middle of the edito

![Visual Studio Tab Switcher](img/VSTabSwitcher.png)

I'm partial towards Visual Studio's implementation, specifically where a box pops up in the center of the window. It allows for expansion in all four directions, which could be useful when adding future features such as Pane Navigation.
Since our command palette will have an implementation similar to VSCode's, I'd rather reuse the UI from our command palette for the Tab Switcher. This means it'll appear as a dropdown from the middle of the tab view.

In terms of navigating the switcher, both VSCode and Visual Studio behave very similarly. Both open with the press of <kbd>ctrl+tab</kbd> and dismiss on release of <kbd>ctrl</kbd>. They both also allow the user to select the tab with the mouse and with <kbd>enter</kbd>. <kbd>esc</kbd> and a mouse click outside of the switcher both dismiss the window as well.

## Solution Design

In addition to the current in-order tab list, namely `IObservableVector<(TODO)> _tabs`, we'll add `IObservableVector<(TODO)> _mruTabs` to represent the tabs in Most Recently Used order. (I'm currently working on converting Tab to WinRT so I'll come back to what the type will be.)
In addition to the current in-order tab list, namely `IObservableVector<TerminalApp::Tab> _tabs`, we'll add `IObservableVector<TerminalApp::Tab> _mruTabs` to represent the tabs in Most Recently Used order.

We'll create a new class `TabSwitcherControl` inside of TerminalPage, which will be initialized with an `IObservableVector<>&`. This is because it'll take in either `_tabs` or `_mruTabs` depending on what order the user would like the tabs to be displayed. Whenever the user changes the setting that controls the tab order, it'll update to get a reference to the correct vector.
We'll create a new class `TabSwitcherControl` inside of TerminalPage, which will be initialized with an `IObservableVector<>&`. This is because it'll take in either `_tabs` or `_mruTabs` depending on what order the user would like the tabs to be displayed. Whenever the user changes the setting that controls the tab order, it'll update to get a reference to the corr ect vector.

This vector of tabs will be used to create a ListView, so that the UI would diplay a vertical list of tabs, represented by their tab titles. Each element in the ListView would be associated with a Tab, and the action associated with selecting a tab would be to Focus on that tab. There would also be another column to the left/right of the tab title column that holds the numbers 1-9. This is to represent what number is bound to each tab to allow for quick switching. This ListView would be hidden until the user presses a keybinding to show the ListView.

The `TabSwitcherControl` will use `TerminalPage`'s `ShortcutActionDispatch` to dispatch a `SwitchToTab` `ShortcutAction`. This will eventually cause `TerminalPage::_OnTabSelectionChanged` to be called. We can update the MRU in this function to be sure that changing tabs from the TabSwitcher, clicking on a tab, or nextTab/prevTab-ing will keep the MRU up-to-date. Adding or closing tabs are handled in `_OpenNewTab` and `_CloseFocusedTab`, which will need to be modified to update both `_tabs` and `_mruTabs`.

## UI/UX Design

First, we'll give a quick overview of how the tab switcher UI would look like, then we'll dive into more detail on how the user would interact with the switcher.
The Tab Switcher will reuse a lot of the XAML code that's used in the command palette. This means it'll show up as a drop-down from the horizontal center of the tab row. It'll appear as a single overlay over the whole Terminal window.

The tab switcher will appear as a box in the center of the terminal. It'll have a maximum and minimum height and width. We want the switcher to stretch, but it shouldn't stretch to take up the entirety of the terminal. If the user has a ton of tabs or has really long tab names, the box should still be fairly contained and shouldn't run wild.

In the box will be a vertical list of tabs with their titles and their assigned number for quick switching, and only one line will be highlighted to signify that tab is currently selected. The top 9 tabs in the list are numbered for quick switching, and the rest of the tabs will simply have an empty space where a number would be.
In the box will be a list of tabs with their titles and their assigned number for quick switching, and only one line will be highlighted to signify that tab is currently selected. The top 9 tabs in the list are numbered for quick switching, and the rest of the tabs will simply have an empty space where a number would be.

The list would look (roughly) like this:
```
Expand Down Expand Up @@ -92,7 +90,7 @@ The tabs designated by numbers 1 through 4 are no longer visible (but still quic

#### Opening the Tab Switcher

The user will press a keybinding named `openTabSwitcher` to bring up the UI.
The user will press a keybinding named `tabSwitcher` to bring up the UI.
The user will be able to change it to whatever they like.
There will also be an optional `anchor` arg that may be provided to this keybinding.

Expand All @@ -116,7 +114,7 @@ The user will be able to navigate through the switcher with the following keybin
- Switching Down: <kbd>tab</kbd> or <kbd>downArrow</kbd>
- Switching Up: <kbd>shift+tab</kbd> or <kbd>upArrow</kbd>

As the user is cycling through the tab list, the selected tab will be highlighted but the terminal won't actually switch focus to the selected tab.
As the user is cycling through the tab list, the selected tab will be highlighted but the terminal won't actually switch focus to the selected tab. This also applies to pointer interaction. Hovering over an item with a mouse will highligh the item but not switch to the tab.

#### Closing the Switcher and Bringing a Tab into Focus

Expand Down Expand Up @@ -189,7 +187,7 @@ Visual Studio Code only allows the user to shrink the window until it hits a min

![Small Visual Studio Code with Tab Switcher](img/VSCodeMinimumTabSwitcherSize.png)

Terminal can't really replicate Visual Studio's version of the tab switcher in this situation. The TabSwitcher needs to be contained within the Terminal. So, if the TabSwitcher is always centered and has a percentage padding from the borders of the Terminal, it'll shrink as Terminal shrinks. Whatever is visible of the TabSwitcher will be visible. In the case where the Terminal is so small that the switcher isn't visible, the Terminal isn't of much use anyway.
Terminal can't really replicate Visual Studio's version of the tab switcher in this situation. The TabSwitcher needs to be contained within the Terminal. So, if the TabSwitcher is always centered and has a percentage padding from the borders of the Terminal, it'll shrink as Terminal shrinks. Since the Terminal also has a minimum width, the switcher should always have enough space to be usefully visible.

## Future considerations

Expand All @@ -207,9 +205,7 @@ Pane navigation is a clear next step to build on top of the tab switcher, but th

### Tab Search by Name/Title

This is something that would be particularly nice to have, especially when there's a large list of tabs. You could be cycling through your thousands of tabs, trying to find the tab that's actually 456th in the list.

A search box would be attached to the UI and the list of tabs that are presented would be filtered based on what the string is in the searchbox. We already have search being worked on [#605], so we could probably leverage some of that work for tab search.
This is something that would be particularly nice to have, especially when there's a large list of tabs. You could be cycling through your thousands of tabs, trying to find the tab that's actually 456th in the list. The command palette is specced to be able to filter through commands as a user types into its search box, so we might be able to be reuse that functionality to filter through tab titles!

### Tab Preview on Hover

Expand All @@ -221,6 +217,7 @@ after the tab switcher has landed.
Feature Request: An advanced tab switcher [#1502]
Ctrl+Tab toggle between last two windows like Alt+Tab [#973]
The Command Palette Thread [#2046]
The Command Palette Spec [#5674]
Feature Request: Search [#605]

<!-- Footnotes -->
Expand Down

1 comment on commit fda6a36

@github-actions
Copy link

Choose a reason for hiding this comment

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

New misspellings found, please review:

  • boofoo
  • diplay
  • ect
  • highligh
  • img
  • keybinds
  • mru
  • swticher
  • zadiji
To accept these changes, run the following commands
perl -e '
my @expect_files=qw('".github/actions/spell-check/expect/alphabet.txt
.github/actions/spell-check/expect/expect.txt
.github/actions/spell-check/expect/web.txt"');
@ARGV=@expect_files;
my @stale=qw('"atg BKMK consoleaccessibility FFF hyperlink NRCS remoting SCS shobjidl xab xb xbc xca xce xdd xffff "');
my $re=join "|", @stale;
my $suffix=".".time();
my $previous="";
sub maybe_unlink { unlink($_[0]) if $_[0]; }
while (<>) {
  if ($ARGV ne $old_argv) { maybe_unlink($previous); $previous="$ARGV$suffix"; rename($ARGV, $previous); open(ARGV_OUT, ">$ARGV"); select(ARGV_OUT); $old_argv = $ARGV; }
  next if /^($re)(?:$| .*)/; print;
}; maybe_unlink($previous);'
perl -e '
my $new_expect_file=".github/actions/spell-check/expect/fda6a3643411bae482ab05b16adebf45575933ef.txt";
open FILE, q{<}, $new_expect_file; chomp(my @words = <FILE>); close FILE;
my @add=qw('"boofoo diplay ect highligh img keybinds mru nrcs Remoting Scs Shobjidl swticher zadiji "');
my %items; @items{@words} = @words x (1); @items{@add} = @add x (1);
@words = sort {lc($a) cmp lc($b)} keys %items;
open FILE, q{>}, $new_expect_file; for my $word (@words) { print FILE "$word\n" if $word =~ /\w/; };
close FILE;'
git add .github/actions/spell-check/expect || echo '... you want to ensure .github/actions/spell-check/expect/fda6a3643411bae482ab05b16adebf45575933ef.txt is added to your repository...'
✏️ Contributor please read this

By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.

⚠️ The command is written for posix shells. You can copy the contents of each perl command excluding the outer ' marks and dropping any '"/"' quotation mark pairs into a file and then run perl file.pl from the root of the repository to run the code. Alternatively, you can manually insert the items...

If the listed items are:

  • ... misspelled, then please correct them instead of using the command.
  • ... names, please add them to .github/actions/spell-check/dictionary/names.txt.
  • ... APIs, you can add them to a file in .github/actions/spell-check/dictionary/.
  • ... just things you're using, please add them to an appropriate file in .github/actions/spell-check/expect/.
  • ... tokens you only need in one place and shouldn't generally be used, you can add an item in an appropriate file in .github/actions/spell-check/patterns/.

See the README.md in each directory for more information.

🔬 You can test your commits without appending to a PR by creating a new branch with that extra change and pushing it to your fork. The :check-spelling action will run in response to your push -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. 😉

⚠️ Reviewers

At present, the action that triggered this message will not show its ❌ in this PR unless the branch is within this repository.
Thus, you should make sure that this comment has been addressed before encouraging the merge bot to merge this PR.

Please sign in to comment.