Skip to content

Commit 5ef61a3

Browse files
committed
bb-winui: update WPF based Bitbucket prompt
Update the WPF-based Bitbucket authentication prompt in a similar fashion to the Avalonia ones.
1 parent 6ceab40 commit 5ef61a3

File tree

9 files changed

+175
-138
lines changed

9 files changed

+175
-138
lines changed

src/windows/Atlassian.Bitbucket.UI.Windows/Commands/OAuthCommandImpl.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/windows/Atlassian.Bitbucket.UI.Windows/Controls/TesterWindow.xaml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,30 @@
66
mc:Ignorable="d"
77
Title="Bitbucket Authentication Dialog Tester"
88
Height="240" Width="420" ResizeMode="NoResize">
9-
<DockPanel>
10-
<Button Content="Show Credentials Dialog" Padding="10" Click="ShowCredentials" />
11-
<Button Content="Show OAuth Dialog" Padding="10" Click="ShowOAuth" />
12-
</DockPanel>
9+
<StackPanel>
10+
<Grid>
11+
<Grid.RowDefinitions>
12+
<RowDefinition Height="Auto"/>
13+
<RowDefinition Height="Auto"/>
14+
<RowDefinition Height="Auto"/>
15+
</Grid.RowDefinitions>
16+
<Grid.ColumnDefinitions>
17+
<ColumnDefinition Width="Auto"/>
18+
<ColumnDefinition Width="*"/>
19+
</Grid.ColumnDefinitions>
20+
<Label Grid.Row="0" Grid.Column="0" Content="Auth Modes" />
21+
<StackPanel Grid.Row="0" Grid.Column="1"
22+
Orientation="Horizontal" VerticalAlignment="Center">
23+
<CheckBox Content="Browser" x:Name="showOAuth" MinWidth="90" IsChecked="True" />
24+
<CheckBox Content="Basic" x:Name="showBasic" MinWidth="80" IsChecked="True" />
25+
</StackPanel>
26+
<Label Grid.Row="1" Grid.Column="0" Content="URL" />
27+
<TextBox Grid.Row="1" Grid.Column="1" x:Name="url" />
28+
<Label Grid.Row="2" Grid.Column="0" Content="Username" />
29+
<TextBox Grid.Row="2" Grid.Column="1" x:Name="username" />
30+
</Grid>
31+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10">
32+
<Button Content="Show" Click="ShowCredentials" Padding="8,4"/>
33+
</StackPanel>
34+
</StackPanel>
1335
</Window>

src/windows/Atlassian.Bitbucket.UI.Windows/Controls/TesterWindow.xaml.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Windows;
23
using Atlassian.Bitbucket.UI.ViewModels;
34
using Atlassian.Bitbucket.UI.Views;
@@ -19,17 +20,17 @@ private void ShowCredentials(object sender, RoutedEventArgs e)
1920
{
2021
var vm = new CredentialsViewModel(_environment)
2122
{
22-
ShowOAuth = true
23+
ShowOAuth = showOAuth.IsChecked ?? false,
24+
ShowBasic = showBasic.IsChecked ?? false,
25+
UserName = username.Text
2326
};
24-
var view = new CredentialsView();
25-
var window = new DialogWindow(view) { DataContext = vm };
26-
window.ShowDialog();
27-
}
2827

29-
private void ShowOAuth(object sender, RoutedEventArgs e)
30-
{
31-
var vm = new OAuthViewModel(_environment);
32-
var view = new OAuthView();
28+
if (Uri.TryCreate(url.Text, UriKind.Absolute, out Uri uri))
29+
{
30+
vm.Url = uri;
31+
}
32+
33+
var view = new CredentialsView();
3334
var window = new DialogWindow(view) { DataContext = vm };
3435
window.ShowDialog();
3536
}

src/windows/Atlassian.Bitbucket.UI.Windows/Program.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ public static async Task Main(string[] args)
2222
}
2323

2424
app.RegisterCommand(new CredentialsCommandImpl(context));
25-
app.RegisterCommand(new OAuthCommandImpl(context));
2625

2726
int exitCode = app.RunAsync(args)
2827
.ConfigureAwait(false)

src/windows/Atlassian.Bitbucket.UI.Windows/Views/CredentialsView.xaml

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<ResourceDictionary Source="../Assets/Styles.xaml"/>
1616
</ResourceDictionary.MergedDictionaries>
1717
<sharedConverters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
18+
<sharedConverters:NonNullToVisibleConverter x:Key="NonNullToVisibleConverter"/>
1819
</ResourceDictionary>
1920
</UserControl.Resources>
2021
<DockPanel LastChildFill="True">
@@ -34,38 +35,87 @@
3435
</StackPanel>
3536

3637
<StackPanel DockPanel.Dock="Top" Margin="0,0,0,30">
37-
<Image HorizontalAlignment="Center" Source="/Assets/atlassian-logo.png" />
38+
<Image HorizontalAlignment="Center" Source="/Assets/atlassian-logo.png" Height="90" />
3839
<TextBlock HorizontalAlignment="Center"
39-
FontSize="14"
40+
FontSize="14" Margin="0,-10,0,0"
4041
Text="Log in to your account"/>
42+
<TextBlock HorizontalAlignment="Center"
43+
Text="{Binding Url}" FontSize="14"
44+
Visibility="{Binding Url, Converter={StaticResource NonNullToVisibleConverter}}"
45+
Margin="0,10,0,0"/>
4146
</StackPanel>
4247

43-
<StackPanel Width="288">
44-
<sharedControls:PromptTextBox x:Name="userNameTextBox"
45-
Margin="0,0,0,10"
46-
HorizontalAlignment="Stretch"
47-
PromptText="Email or username"
48-
Text="{Binding UserName}" />
49-
<sharedControls:PasswordPromptTextBox x:Name="passwordTextBox"
50-
Margin="0,0,0,20"
51-
HorizontalAlignment="Stretch"
52-
PromptText="Password"
53-
Password="{Binding Password, UpdateSourceTrigger=PropertyChanged, Delay=300, Mode=OneWayToSource}" />
54-
<Button IsDefault="True"
55-
Margin="0,0,0,10"
56-
HorizontalAlignment="Center"
57-
HorizontalContentAlignment="Center"
58-
VerticalContentAlignment="Center"
59-
Command="{Binding LoginCommand}"
60-
Content="Continue"
61-
Width="140" Height="40"
62-
Style="{StaticResource AccentButton}"/>
63-
<TextBlock FontSize="14" TextAlignment="Center"
64-
Visibility="{Binding ShowOAuth, Converter={StaticResource BooleanToVisibilityConverter}}">
65-
<Hyperlink Command="{Binding OAuthCommand}">
66-
Sign in with OAuth
67-
</Hyperlink>
68-
</TextBlock>
69-
</StackPanel>
48+
<TabControl x:Name="tabControl"
49+
BorderThickness="0"
50+
Background="Transparent">
51+
<TabControl.Resources>
52+
<Style TargetType="TabPanel">
53+
<Setter Property="HorizontalAlignment" Value="Center"/>
54+
</Style>
55+
<Style TargetType="TabItem">
56+
<Setter Property="Template">
57+
<Setter.Value>
58+
<ControlTemplate TargetType="TabItem">
59+
<Border Name="Border" Margin="10,0,10,10"
60+
BorderThickness="0,0,0,2"
61+
BorderBrush="Transparent">
62+
<ContentPresenter x:Name="ContentSite"
63+
VerticalAlignment="Center"
64+
HorizontalAlignment="Center"
65+
ContentSource="Header"
66+
Margin="0,0,0,5"/>
67+
</Border>
68+
<ControlTemplate.Triggers>
69+
<Trigger Property="IsSelected" Value="True">
70+
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
71+
</Trigger>
72+
</ControlTemplate.Triggers>
73+
</ControlTemplate>
74+
</Setter.Value>
75+
</Setter>
76+
</Style>
77+
</TabControl.Resources>
78+
79+
<TabItem IsEnabled="{Binding ShowOAuth}"
80+
Visibility="{Binding ShowOAuth, Converter={StaticResource BooleanToVisibilityConverter}}">
81+
<TabItem.Header>
82+
<TextBlock Text="Browser" FontSize="12" />
83+
</TabItem.Header>
84+
<StackPanel x:Name="oauthPanel"
85+
Margin="0,10">
86+
<Button x:Name="oauthButton"
87+
Content="Sign in with your browser"
88+
IsDefault="True"
89+
Command="{Binding OAuthCommand}"
90+
HorizontalAlignment="Center"
91+
Margin="0,0,0,10"
92+
Style="{StaticResource AccentButton}"/>
93+
</StackPanel>
94+
</TabItem>
95+
96+
<TabItem IsEnabled="{Binding ShowBasic}"
97+
Visibility="{Binding ShowBasic, Converter={StaticResource BooleanToVisibilityConverter}}">
98+
<TabItem.Header>
99+
<TextBlock Text="Password" FontSize="12" />
100+
</TabItem.Header>
101+
<StackPanel x:Name="basicPanel"
102+
Margin="0,10">
103+
<sharedControls:PromptTextBox x:Name="userNameTextBox"
104+
Margin="0,0,0,10"
105+
PromptText="Email or username"
106+
Text="{Binding UserName}"/>
107+
<sharedControls:PasswordPromptTextBox x:Name="passwordTextBox"
108+
Margin="0,0,0,10"
109+
PromptText="Password or token"
110+
Password="{Binding Password, UpdateSourceTrigger=PropertyChanged, Delay=300, Mode=OneWayToSource}"/>
111+
<Button Content="Sign in"
112+
IsDefault="True"
113+
Command="{Binding LoginCommand}"
114+
HorizontalAlignment="Center"
115+
Width="140" Height="40"
116+
Style="{StaticResource AccentButton}"/>
117+
</StackPanel>
118+
</TabItem>
119+
</TabControl>
70120
</DockPanel>
71121
</UserControl>

src/windows/Atlassian.Bitbucket.UI.Windows/Views/CredentialsView.xaml.cs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using System.Windows;
12
using System.Windows.Controls;
3+
using System.Windows.Input;
24
using Atlassian.Bitbucket.UI.ViewModels;
35
using GitCredentialManager.UI.Controls;
46

@@ -11,20 +13,60 @@ public CredentialsView()
1113
InitializeComponent();
1214
}
1315

16+
// Set focus on a UIElement the next time it becomes visible
17+
private static void OnIsVisibleChangedOneTime(object sender, DependencyPropertyChangedEventArgs e)
18+
{
19+
if (sender is UIElement element)
20+
{
21+
// Unsubscribe to prevent re-triggering
22+
element.IsVisibleChanged -= OnIsVisibleChangedOneTime;
23+
24+
// Set logical focus
25+
element.Focus();
26+
27+
// Set keyboard focus
28+
Keyboard.Focus(element);
29+
}
30+
}
31+
1432
public void SetFocus()
1533
{
1634
if (!(DataContext is CredentialsViewModel vm))
1735
{
1836
return;
1937
}
2038

21-
if (string.IsNullOrWhiteSpace(vm.UserName))
39+
//
40+
// Select the best available authentication mechanism that is visible
41+
// and make the textbox/button focused when it next made visible.
42+
//
43+
// In WPF the controls in a TabItem are not part of the visual tree until
44+
// the TabControl has been switched to that tab, so we must delay focusing
45+
// on the textbox/button until it becomes visible.
46+
//
47+
// This means as the user first moves through the tabs, the "correct" control
48+
// will be given focus in that tab.
49+
//
50+
void SetFocusOnNextVisible(UIElement element)
51+
{
52+
element.IsVisibleChanged += OnIsVisibleChangedOneTime;
53+
}
54+
55+
// Set up focus events on all controls
56+
SetFocusOnNextVisible(
57+
string.IsNullOrWhiteSpace(vm.UserName)
58+
? userNameTextBox
59+
: passwordTextBox);
60+
SetFocusOnNextVisible(oauthButton);
61+
62+
// Switch to the preferred tab
63+
if (vm.ShowOAuth)
2264
{
23-
userNameTextBox.Focus();
65+
tabControl.SelectedIndex = 0;
2466
}
25-
else
67+
else if (vm.ShowBasic)
2668
{
27-
passwordTextBox.Focus();
69+
tabControl.SelectedIndex = 1;
2870
}
2971
}
3072
}

src/windows/Atlassian.Bitbucket.UI.Windows/Views/OAuthView.xaml

Lines changed: 0 additions & 54 deletions
This file was deleted.

src/windows/Atlassian.Bitbucket.UI.Windows/Views/OAuthView.xaml.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/windows/Core.UI.Windows/Converters/NonEmptyStringToVisibleConverter.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,18 @@ public virtual object ConvertBack(object value, Type targetType, object paramete
1818
return Binding.DoNothing;
1919
}
2020
}
21+
22+
[ValueConversion(typeof(string), typeof(Visibility))]
23+
public class NonNullToVisibleConverter : IValueConverter
24+
{
25+
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
26+
{
27+
return ConverterHelper.GetConditionalVisibility(value != null, parameter);
28+
}
29+
30+
public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
31+
{
32+
return Binding.DoNothing;
33+
}
34+
}
2135
}

0 commit comments

Comments
 (0)