terminal icon indicating copy to clipboard operation
terminal copied to clipboard

Add comprehensive XAML "theming" functionality

Open zadjii-msft opened this issue 4 years ago • 13 comments

As I mentioned in https://github.com/microsoft/terminal/issues/3322#issuecomment-546391285, we've been dancing around the idea of "XAML theming" for a while in a bunch of issues, but never had one place to track all the requested functionality. This will serve as the master thread for those requests.

It looks to me like we've danced around the idea in #1963, #1337, #3061/#3062, #2994, but never had a comprehensive answer.

Ideally, these are something that's more powerful than just setting the "color scheme". This would control sizing, coloration of UI elements of the app itself, not just the colors of the terminal contents. Consider things like themes in VsCode, Sublime Text, where there are schemes that can control the colorization of the buffer, and themes that can change the appearance of the app itself.

UI elements to be able to control:

  • [ ] #3061 Pane Border colors (both the background, and the "focused" color)
  • [ ] #3063 Pane border width
  • [x] #702 👀 Tab Background color
    • also: #1337
    • also: #2994
  • [x] #13684
  • [x] #3774 👀 Tab row BG color
    • [x] #1963 👀 users should be able to set it to the system accent color
    • Users want the tab row to match the BG color of the focused pane/terminal. This is the inverse of #702
  • [x] #3335 👀 Feature Request: Setting to hide/remove close ("x") button from tabs
  • [x] #4862 👀 Support using different colors in the titlebar for focused/unfocused windows
  • [ ] #5911 Support for compact, default, and touch friendly tab row sizes
  • [ ] #7213
    • Also used for setting the corner radius
    • Also tracking "get rid of the bottom, outwards corner radius"

Other ideas:

  • [ ] #5155 Tab row height
  • [ ] Tab row font size, font face
  • [ ] Margin between tabs?
  • [ ] Padding within the tab?
  • [ ] Control colors for light vs dark vs high-contrast modes
  • [ ] Enable/disable a shadow underneath the tab row, between tabs and content
    • more relevant after #12916 which added the shadow to the whole row
  • [ ] Enable/disable a shadow cast by terminals on pane borders or a shadow cast by pane borders on Terminal panes
  • [ ] Similarly to the tabs, styling the Status Bar (#3459)
    • Maybe enable it to have the same color as the active TermControl, causing the same "seamless" effect (https://github.com/microsoft/terminal/issues/3459#issuecomment-550501577)
    • Change font size, face, colors
    • Control the borders on the status bar - no top border would give the impression it's "seamless"
  • [ ] Enable hiding the tab icon altogether (#8157)
  • [ ] Enable forcing tab icons to monochrome
  • [ ] Force a colorscheme? This seems weird, and probably not what people want.
  • [ ] Allow color fields to refer back to the active color scheme (like cursorColor = yellow) (#7522)
  • [ ] Please provide a new setting: verticalScrollBarWidth #9218
  • [ ] Light mode in settings UI only #9231
  • [ ] #13991
  • [ ] #12632

Currently we don't have all that many UI elements, but in a hypothetical world with a command palette (#2046) and a search box (#605), there'll be even more UI elements to be able to control.

I know @cinnamon-msft had some mockups of "themes".

I imagine that these would be something that would be easier to control with XAML resources somehow, though I'm not sure how technically possible it would be to have the user specify a XAML file and have us load it into our resources at runtime. But that might be an option to pursue as an alternative to adding tons of new settings that will need to be parsed and applied manually at runtime.

Potential solution design:

This is a real showerthought of a design, which needs real spec'ing, but here's what I came up with this (06 Dec 2019) morning

{
    "applicationTheme": "My Boxy Theme",
    "themes": [
        {
            "name": "My Boxy Theme",
            "requestedTheme": "dark",
            "tab.radius": 0,
            "tab.padding": 5,
            "tab.background": "terminalBackground",
            "tab.textColor": "key:SystemAccentColorLight3",
            "tab.icon": "monochrome",
            "tab.closeButton": "hidden",
            "tabRow.background": "accent",
            "tabRow.shadows": false
        },
        {
            "name": "My small light theme",
            "tabBackground": "#80ff0000",
            "tabRowBackground": "#ffffffff",
            "tabHeight": 16,
            "requestedTheme": "light",
            "colorSheme": "Solarized Light",
            "tabIcon": "hidden",
            "tabCloseButton": "hover"
        }
    ]
}

I've given both a tab.<property> and a tab<Property> style here, for comparison. Unsure of which is better.

Colors can be one of:

  • an #aarrggbb color
  • accent for the accent color
  • terminalBackground to use the default background color of the focused terminal
  • terminalForeground to use the default foreground color of the focused terminal
    • does anyone want this?
  • key:SomeXamlKey to try and look SomeXamlKey up from our resources as a Color, and use that color for the value.
    • Does anyone want this?
    • is accent just key:SystemAccentColor?

Then we have a bunch of UI settings.

We'll use these settings to set XAML resource values, like the TabViewBackground. Then, when they change, the UI should just respond to these values changing right?

Open Questions:

  • For hot-reloading settings - does updating resources in xaml auto-relayout things?

  • For "tabBackground": "terminalBackground", does changing the default background color from within the terminal automatically update this color for UI elements? (hopefully).

  • This design doesn't really have both light and dark variants, instead just sets a system theme it wants. What if the theme specifies system? could it support light and dark versions somehow?

Tab Color Picker

With the tab color picker that sets tab colors, how does that interact with this? We probably don't actually want the color to apply to the whole tab itself, we probably just want an overline. If we have an overline, people probably what to be able to set it. Manually setting the color with the menu should just be an override - "tabColorOverride", so that a settings reload doesn't blow it away. Presumably, there's a way to set the background color of a tab manually to override that of the theme - thet's probably how that PR works today.

How would we make sure that "tab.background": "terminalBackground" works with manually overriding the tab color?

Terminal should be able to look beautiful, and also like this: image

Followup flow chart:

flowchart TD
    subgraph Theming
        id_b[tabRow.background]
        id_c[ themeColor:accent ]
        id_d[ themeColor:terminalBackground ]

        %% id1[Theming #12992]
    end

    id2[tab.background<br>#702<br>PR #13178]
    id3[tabRow.inactiveBackground<br>#4862<br>PR 13049]
    id4(tab.inactiveBackground<br>#4862)
    tabclose[tab.closeButton<br>#3335<br>PR #13348]

    id15{{Terminal v1.16}}

    %% Initial theming support
    %% id_a-->id1
    %% id_b-->id1
    %% id_c-->id1
    %% id_d-->id1
    
    %% 1.16 features
    Theming --> id3
    Theming --> id2
    id2 --> id4
    id3 --> id15
    id4 --> id15
    id2 --> tabclose  --> id15
    Theming --> id_transparent(Transparent titlebars<br>window.useMica<br>#10509)
    Theming --> id_acrylic_tabRow(Acrylic titlebar) --> id15


    %% Etc features
    subgraph Pane
        direction TB
        pane.borderColor(pane.borderColor<br>#3061)
        pane.inactiveBorderColor(pane.inactiveBorderColor)
        pane.borderWidth(pane.borderWidth<br>#3063)
        pane.borderColor --> pane.inactiveBorderColor
    end
    subgraph Tab
        direction TB
        tabcorners(tab.corners<br>#7213)
    end
    subgraph Window
        direction TB
        window.background(window.background)
        window.frameColor(window.frameColor) --> rainbow(themeColor:rainbow<br>#12950)
    end

    id15 --> Pane
    id15 --> Tab
    id15 --> Window
    id15 --> lightDark(OS-theme sensitive theming)
    click id4 "http://www.github.com/microsoft/terminal/issues/4862"

zadjii-msft avatar Oct 25 '19 15:10 zadjii-msft

Not sure if this task is just talking about extending the theme flexibility but my feature-request may be related... https://github.com/microsoft/terminal/issues/3687 ?

NeilMacMullen avatar Nov 24 '19 17:11 NeilMacMullen

Perhaps you could allow the importing of a ResourceDictionary - or a JSON which gets translated into a valid Xaml ResourceDictonary ingested on app startup.

mdtauk avatar Nov 24 '19 21:11 mdtauk

Hey, @zadjii-msft , just some thoughts about this, I hope you don't mind:

  • UI elements to be able to control
    • if the user is able to theme the tab background, they should be able to theme the foreground as well
    • Since you mentioned the tabrow, I guess that the split button should also be included. Or for you tabrow == tabrow + split button?
    • maybe theme the terminal background too? what about min/max buttons? ideally their foreground should match the foreground of the tabs as well
  • theme format
    • as @mdtauk mentioned, parsing json and storing the values in App.Resources(), would be pretty cool. In WPF it was possible to load a xaml dict on the fly, no idea about UWP and do you really want the users to play with XAML dicts around?
    • I think that "tab.background" is much more readable than "tabBackground", but this is really personal preference.
  • Open Questions
    • hot reloading - in my experience dynamically shoving something into a resource dictionary does absolutely nothing in regards to automatically refreshing colors/redoing layout. This is why I'm doing the weird 'toggle the visual state' thing in my PR (#2994). For layouting, one could call UpdateLayout or InvalidateArrange, I guess
    • "tabBackground": "terminalBackground" - I'm not sure that I understand, but since tabs and the background use different resources, don't think so (except where the tab is deselected. Then the transparent brush is used to draw the tab).
  • the tab color picker thing:     my inspiration about the tab color came from the peacock vs code extension or solution colors for visual studio. Lets take vs code for example:      You can select your theme and then vs code is colored in some glorious way. But since you work on multiple projects at once, you use peacock to paint (not only) the title area, so that if you alt-tab, with a single glance you which vs code you have just selected. Going back to tab, I want to know with a single glance which repo am I currently using (for example). This is why I want to paint the whole tab (and as you've already seen - the whole title bar area, which solves the problem of knowing which is the currently selected tab, without doing funny things with the transparency of the deselected tabs).      If you load the theme resources in the App.Resources(), everything should be fine. Because the color that the user has picked is stored in the resources dict of the tab itself, and the tab's resources dict has precedence. Then you can have the themed window and a manually selected color of a tab.

Those are just my thoughts, how exactly would it end up is your call, of course.

gbaychev avatar Feb 10 '20 16:02 gbaychev

As a start, could the tab background be calculated based on the background setting of the theme? That would already help by not having that jarring #000 there.

Stanzilla avatar Feb 11 '20 17:02 Stanzilla

When can we have this feature released?

kzhuangmc avatar Mar 30 '20 20:03 kzhuangmc

When it's done being developed.

Stanzilla avatar Mar 30 '20 20:03 Stanzilla

This could be a really good start, Chrome OS Terminal now colors the tab by the background color

image

https://www.androidpolice.com/2020/04/04/hack-the-planet-in-style-with-the-new-linux-terminal-in-chrome-os-83/

Stanzilla avatar Apr 08 '20 17:04 Stanzilla

Coming from #7363 , the acrylic setting should extend into the tab space.

When acrylic is enabled, the tab color (which is currently never acrylic) becomes inconsistent with the background (where acrylic is applied).

See below - it appears, at a glance, that the first tab is active, because the color is a close match to the acrylic average. But in reality, the black tab in the middle is active.

image

mikemaccana avatar Aug 21 '20 14:08 mikemaccana

@mikemaccana ahhhhh, okay I think I'm getting it better now (with more coffee). What you're really looking for is the #702/#3774 subset of this issue. The spec over at #5772 covers this as "tab.background": "terminalBackground", so the tab color will automatically match the terminal's background brush (including acrylic).

zadjii-msft avatar Aug 21 '20 14:08 zadjii-msft

I leave this here as a reference: Fluent XAML Theme Editor

pablojimpas avatar Oct 29 '20 20:10 pablojimpas

@zadjii-msft missed this back in August, but yes exactly! Thanks. 🙂

mikemaccana avatar Oct 29 '20 21:10 mikemaccana

As discussed in the following issue, there are too many ideas to achieve, including but not limited to the title bar, the current label, the hover label, the background color, and the different settings for the title bar. There is no doubt that it will eventually Import to build a theme system. My confusion lies in whether such a "grand" personalized setting system is needed for WindowsTerminal? I think I can sort out which parts can be prioritized (although Microsoft may not be interested in this). #1963,#1337,#3061 / #3062,#2994

The Fluent Design System is constantly evolving, it is always in development, and less than 1/3 of the system applications adopt them, so Windows will never have a unified user interface, because the speed of applications using the latest design language will never keep up The speed of design updates. Moreover, before the release of WinUI3, I am afraid that all Win32 applications will not have much design changes-this is because Microsoft seems to be hesitant on whether to use XAML to build new applications.

Based on this, we discuss the design of Windows Terminal to minimize design changes and be compatible with existing designs as much as possible.

Windows Terminal allows you to set the background, but when the background uses acrylic, you will encounter an extremely ugly title bar (tab page bar), so the first consideration is to extend the acrylic effect to the title bar (tab page bar).

image

Here is a question, how to deal with the current label color, I personally think that the label color and the background of the current page can be consistent. If the current background color is a picture, the theme color will be automatically extracted from the picture. If you do this, you only need to specify two colors:

Title bar color Window background color (=label color) WindowsTerminal1 WindowsTerminal2 WindowsTerminal5

There is another solution here, which I saw in the Files App, using a completely floating tab design.

In this case, the color of the title bar (tab page bar) is the same as the background color of the window. If a picture is used, the picture is tiled or full in the current window. Add a rounded rectangle background to the current label to show the selection, as shown in the figure.

WindowsTerminal3 WindowsTerminal4 WindowsTerminal6

I think these adjustments will not increase too much workload and are in harmony with Fluent Design. I want Microsoft to work as quickly as possible. I really look forward to Sun Valley, but so far, I think the biggest changes may be the "alarm and clock app" and the news flow of the taskbar.

Shomnipotence avatar Jan 22 '21 15:01 Shomnipotence

latest in dev/migrie/fhl/theming-2022-prototype. Serialization is easy. Binding to the Terminal color is harder. We could just bubble it as PropertyChanged every time, but then there's the complication of having the AppHost then check which things it needs to update every time. That is annoying.

What I really wanted was just a Brush I could define and then have a bunch of different things bind to that so they'd change automagically

Thoughts:

  • [x] Using the StaticResource in App.xaml combined with the Binding Path=Brush thing didn't work AT ALL.
  • [ ] Probably something like TitlebarViewModel that the Titlebar is bound to, so the TerminalPage can change the titlebarViewModel.BackgroundBRush() to match the Terminal one. that would work, but isn't that just the same as needing the propertychanged handler?
  • [x] Maybe at runtime we add a TerminalBackground to the App resources, and then.... gah how do we bind the Titlebar BG to the TerminalBackground.Brush? This doesn't work at all. That's literally what I did try before. Why'd I think that would work?

zadjii-msft avatar Mar 28 '22 16:03 zadjii-msft