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

[Windows] Add TitleBar Control #23019

Merged
merged 36 commits into from
Jul 24, 2024
Merged

[Windows] Add TitleBar Control #23019

merged 36 commits into from
Jul 24, 2024

Conversation

Foda
Copy link
Member

@Foda Foda commented Jun 12, 2024

This PR is a draft and seeking API feedback for an initial release

Description of Change

This PR adds a new TitleBar control and API to set the TitleBar on a Window. It currently only applies to the Windows platform, but it's built entirely as a MAUI control to allow macOS support later.

Please read the full API description here: #13023

Feature demo (PlatformSpecifics page in Maui.Controls.Sample):

titlebar.mp4

API/Usage

The primary API is the Window.SetTitleBar function -- which removes the TitleBar control from the visual tree and inserts it as the native TitleBar (this approach was shamelessly copied from the WinAppSDK). This was used vs an attached property (ex: <Window.TitleBar>) because it was difficult to get a valid Window object when the property was first set.

Example using XAML:

MainWindow.xaml

<Window.TitleBar>
  <TitleBar
    x:Name="TeamsTitleBar"
    HeightRequest="46"
    Title="Hello World">
    <TitleBar.Content>
      <Entry
        x:Name="SearchTitleBar"
        Placeholder="Search"
        VerticalOptions="Center"
        MinimumWidthRequest="300"
        MaximumWidthRequest="450"
        HeightRequest="32"/>
    </TitleBar.Content>
  </TitleBar>
</Window.TitleBar>

Example using code

MainPage.xaml.cs

protected override void OnAppearing()
{
    base.OnAppearing();
    
    Window.TitleBar = new TitleBar()
    {
        Title = "MAUI App",
        Icon = "appicon.png",
        LeadingContent = new AvatarButton()
    };
}

Issues Fixed

Fixes #13023

@Foda Foda added this to the .NET 9 Planning milestone Jun 12, 2024
@Foda Foda requested review from rmarinho and PureWeen June 12, 2024 22:13
Copy link
Member

@tj-devel709 tj-devel709 left a comment

Choose a reason for hiding this comment

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

Wow this is super cool! Few questions from me!

Mike Corsaro added 2 commits June 20, 2024 11:10
@drasticactions
Copy link
Contributor

drasticactions commented Jun 23, 2024

This PR adds a new TitleBar control and API to set the TitleBar on a Window. It currently only applies to the Windows platform, but it's built entirely as a MAUI control to allow macOS support later.

How do we plan to add support for this with Catalyst?

If you want to use something like NSToolbarDelegate to build a title bar like this:

Screenshot from 2024-06-23 16-48-49

That uses specific NSToolBar items and a layout system where you set identifiers and set the flexibility of where items should appear. The TitleBar API allows for arbitrary existing controls and layout which AFAIK wouldn't work with that delegate.

So if you want arbitrary controls, you would probably need to do something special with the UIWindow, merge the title bar into the inner content, and handle the managing the layout yourself. Or there could be other ways to create a more "native" looking titlebar on Catalyst that uses arbitrary controls. Making sure we have a plan for that now before deciding on this approach only to see it won't work on MacOS wouldn't be ideal.

@Foda
Copy link
Member Author

Foda commented Jun 24, 2024

This PR adds a new TitleBar control and API to set the TitleBar on a Window. It currently only applies to the Windows platform, but it's built entirely as a MAUI control to allow macOS support later.

How do we plan to add support for this with Catalyst?

If you want to use something like NSToolbarDelegate to build a title bar like this:

Screenshot from 2024-06-23 16-48-49

That uses specific NSToolBar items and a layout system where you set identifiers and set the flexibility of where items should appear. The TitleBar API allows for arbitrary existing controls and layout which AFAIK wouldn't work with that delegate.

So if you want arbitrary controls, you would probably need to do something special with the UIWindow, merge the title bar into the inner content, and handle the managing the layout yourself. Or there could be other ways to create a more "native" looking titlebar on Catalyst that uses arbitrary controls. Making sure we have a plan for that now before deciding on this approach only to see it won't work on MacOS wouldn't be ideal.

Yeah, we did talk briefly about this. One of the reasons why I made it a native MAUI control was so that we could just plop it into the "titlebar area" on both Win and Mac. I know it's possible to draw whatever custom control you want in the TitleBar (see: Edge w/ tabs) but I'm not sure what API does that, or what exactly the behavior is (maybe https://developer.apple.com/documentation/uikit/mac_catalyst/removing_the_title_bar_in_your_mac_app_built_with_mac_catalyst ??).

I'm going to play around today to see how that API works

Mike Corsaro added 2 commits July 16, 2024 10:00
@dotMorten
Copy link
Contributor

Personally I have a couple concerns about this one - mostly around it not considering catalyst yet.

  1. The first one is, that without catalyst support up-front, I think it's going to be hard to get the abstraction right to begin with and smoothly slot in catalyst later. Since it is only Windows that is supported, you can still do this without a maui control by just accessing the native view directly. Personally I'd think a sample showing how to do this makes more sense for now. And that still leaves the question of: but then where is the searchbar going on other platforms?

  2. Perhaps it makes more sense for this to be more of an inherit behavior to AppShell, where you can have a search bar, and if running on Windows it can move up into the Titlebar, but I still get a Searchbar on other platforms too and adapts correctly to the platform. I really don't want to have to write a bunch of platform-specific code where for adding for instance search to my app, with different placements/implementations for each platform. If I'm doing that, then I might as well just use native UIs to do this and get the full power.

this approach was shamelessly copied from the WinAppSDK

There's absolutely no shame in that. We shouldn't need multiple ways of doing the same thing on the various XAML frameworks (* cough * TextBlock/Label * cough *), even if the WinUI API could have been cleaner. But again I still think it makes sense to diverge for the sake of crossplatform when necessary, but without Catalyst in the early designs in this PR, it might be hard to know whether the API should have differed to support them both more naturally. @drasticactions also some valid raised concerns along those lines.

Copy link
Member

@PureWeen PureWeen left a comment

Choose a reason for hiding this comment

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

When I initially add the content I can't interact with it

image

But if I add/remove the trailing content then the content becomes interactable

Should we fix this issue?
#17723

Comment on lines +36 to +37
base.OnAppearing();
Window.TitleBar = _customTitleBar;
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be removed again when the page closes?
It does feel slightly weird to have the contentpage mess with the app-wide window. That might be more appropriate to use AppShell for instead, since that requires to be the root of the window content and hosts pages inside it.

Mike Corsaro added 2 commits July 19, 2024 10:50
Fix titlebar title spacing w/ default behavior
Fix titlebar content added not allowing hit test
Fix missing public API
@Foda
Copy link
Member Author

Foda commented Jul 19, 2024

Personally I have a couple concerns about this one - mostly around it not considering catalyst yet.

  1. The first one is, that without catalyst support up-front, I think it's going to be hard to get the abstraction right to begin with and smoothly slot in catalyst later. Since it is only Windows that is supported, you can still do this without a maui control by just accessing the native view directly. Personally I'd think a sample showing how to do this makes more sense for now. And that still leaves the question of: but then where is the searchbar going on other platforms?

The nice thing about this control is that because it's fully implemented in MAUI we also have full flexibility over how it appears in the app. I've gotten this to work in a playground app easily using a combo of adding the interface ISafeAreaView to the page, setting the IgnoreSafeArea property, and using the APIs described here.

image

XAML code for macOS title bar
<Grid
    IgnoreSafeArea="True"
    RowDefinitions="60, *">
    <TitleBar
        HorizontalOptions="Fill"
        HeightRequest="64"
        BackgroundColor="#512BD4">
        <TitleBar.Content>
            <HorizontalStackLayout
                Spacing="4"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IgnoreSafeArea="True">
                <Button
                    BorderWidth="0"
                    Text="&#xF153;"
                    FontFamily="Ionicons"
                    FontSize="14"
                    TextColor="White"
                    VerticalOptions="Center"
                    WidthRequest="40"
                    Padding="0"/>
                <Button
                    BorderWidth="0"
                    Text="&#xF154;"
                    FontFamily="Ionicons"
                    FontSize="14"
                    TextColor="White"
                    VerticalOptions="Center"
                    WidthRequest="40"
                    Padding="0"/>
                <Entry
                    Placeholder="Search"
                    WidthRequest="400"
                    VerticalOptions="Center"
                    HeightRequest="40"/>
            </HorizontalStackLayout>
        </TitleBar.Content>
        <TitleBar.TrailingContent>
            <HorizontalStackLayout
                Spacing="4"
                Margin="0,0,16,0"
                VerticalOptions="Center"
                IgnoreSafeArea="True">
                <Button
                    BorderWidth="0"
                    Text="&#xF1B4;"
                    FontFamily="Ionicons"
                    FontSize="16"
                    TextColor="White"
                    VerticalOptions="Center"
                    WidthRequest="50"
                    Padding="0"/>
                <Button
                    WidthRequest="40"
                    HeightRequest="40"
                    BorderWidth="0"
                    CornerRadius="20"
                    BackgroundColor="Azure"
                    Text="MC"
                    FontSize="10"
                    TextColor="Black"
                    Padding="0"
                    VerticalOptions="Center">
                </Button>
            </HorizontalStackLayout>
        </TitleBar.TrailingContent>
    </TitleBar>

    <Button
        WidthRequest="200"
        Text="Click Me"
        Grid.Row="1"/>
</Grid>

For now, I've added the ISafeAreaView interface to TitleBar since that's the main part needed. The next steps would be to wire up a nice way to automatically handle propagating IgnoreSafeArea to content in the title bar, and inserting it into the layout from the Window control.

@PureWeen PureWeen enabled auto-merge (squash) July 24, 2024 21:52
@PureWeen PureWeen merged commit d54c8fa into main Jul 24, 2024
97 checks passed
@PureWeen PureWeen deleted the foda/NewTitlebar branch July 24, 2024 22:04
mattleibow added a commit that referenced this pull request Jul 26, 2024
mattleibow pushed a commit that referenced this pull request Jul 26, 2024
* Custom titlebar control support spike

* handle titlebar hit test elements automatically

* sample

* Added TitleBar property to Window: you can now set titlebar properties on the Window such as leading + trailing content, color, etc.

* Add inactive colors for background + foreground
Fix hide/show titlebar control not working

* Fix issue w/ titlebar unhook on window close
Add titlebar to sample page

* Added `Window.SetTitleBar` function to set the window titlebar. This allows XAML defined titlebars w/ bindings

* Revert some changes

* Revert changes
Fix PublicAPI defs

* Error checkAdditional error checks for page nav

* Update ITitlebar interface

* Titlebar control is now a TemplatedView

* Remove `SetTitlebar` API
Update sample

* Remove child

* Use pattern matching

* Add some docs

* Fix missing publicapi bits

* More public api bits

* Missed some

* Fix use of content presenter

* Adjust passthrough logic

* Fix titlebar height adjust for caption buttons

* Fix override of default template not working

* Cleanup

* Fix default control template not being applied
Add default style for TitleBar

* Fix missing API for tizen

* Fix sample issue
Update sample

* Fix missing title + icon when titlebar is not fully set
Fix titlebar title spacing w/ default behavior
Fix titlebar content added not allowing hit test
Fix missing public API

* Add IgnoreSafeArea for macOS

* Tizen

* Fix titlebar being set on window creation w/ template selector

* Comment out templates

---------

Co-authored-by: Mike Corsaro <[email protected]>
Co-authored-by: Javier Suárez <[email protected]>
# Conflicts:
#	src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt
#	src/Core/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
rmarinho pushed a commit that referenced this pull request Jul 26, 2024
rmarinho pushed a commit that referenced this pull request Jul 26, 2024
* [Windows] Add TitleBar Control (#23019)

* Custom titlebar control support spike

* handle titlebar hit test elements automatically

* sample

* Added TitleBar property to Window: you can now set titlebar properties on the Window such as leading + trailing content, color, etc.

* Add inactive colors for background + foreground
Fix hide/show titlebar control not working

* Fix issue w/ titlebar unhook on window close
Add titlebar to sample page

* Added `Window.SetTitleBar` function to set the window titlebar. This allows XAML defined titlebars w/ bindings

* Revert some changes

* Revert changes
Fix PublicAPI defs

* Error checkAdditional error checks for page nav

* Update ITitlebar interface

* Titlebar control is now a TemplatedView

* Remove `SetTitlebar` API
Update sample

* Remove child

* Use pattern matching

* Add some docs

* Fix missing publicapi bits

* More public api bits

* Missed some

* Fix use of content presenter

* Adjust passthrough logic

* Fix titlebar height adjust for caption buttons

* Fix override of default template not working

* Cleanup

* Fix default control template not being applied
Add default style for TitleBar

* Fix missing API for tizen

* Fix sample issue
Update sample

* Fix missing title + icon when titlebar is not fully set
Fix titlebar title spacing w/ default behavior
Fix titlebar content added not allowing hit test
Fix missing public API

* Add IgnoreSafeArea for macOS

* Tizen

* Fix titlebar being set on window creation w/ template selector

* Comment out templates

---------

Co-authored-by: Mike Corsaro <[email protected]>
Co-authored-by: Javier Suárez <[email protected]>
# Conflicts:
#	src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt
#	src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt
#	src/Core/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt

* Use new things

---------

Co-authored-by: Mike Corsaro <[email protected]>
@samhouts samhouts added the fixed-in-net9.0-nightly This may be available in a nightly release! label Aug 2, 2024
@samhouts samhouts added fixed-in-8.0.80 fixed-in-net9.0-nightly This may be available in a nightly release! and removed fixed-in-net9.0-nightly This may be available in a nightly release! labels Aug 8, 2024
@samhouts samhouts added fixed-in-9.0.0-preview.7.24407.4 and removed fixed-in-net9.0-nightly This may be available in a nightly release! labels Aug 27, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Sep 27, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide a cross platform API for customizing the TitleBar of desktop apps