Browse Source

add vpn ui toggle

bdbai 3 years ago
parent
commit
cf3a709cf5
6 changed files with 344 additions and 186 deletions
  1. 1 2
      Maple.App/App.xaml
  2. 106 6
      Maple.App/MainPage.cpp
  3. 11 0
      Maple.App/MainPage.h
  4. 213 167
      Maple.App/MainPage.xaml
  5. 13 11
      README.md
  6. BIN
      image/screenshot-setting1.png

+ 1 - 2
Maple.App/App.xaml

@@ -2,8 +2,7 @@
     x:Class="Maple_App.App"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:local="using:Maple_App"
-    RequestedTheme="Light">
+    xmlns:local="using:Maple_App">
     <Application.Resources>
         <ResourceDictionary>
             <Color x:Key="SystemAccentColor">#2ebe5a</Color>

+ 106 - 6
Maple.App/MainPage.cpp

@@ -2,13 +2,18 @@
 #include "MainPage.h"
 #include "MainPage.g.cpp"
 #include <filesystem>
-#include <winrt/Windows.Networking.Vpn.h>
+#include <winrt/Windows.ApplicationModel.Core.h>
 #include <winrt/Windows.UI.ViewManagement.h>
+#include <winrt/Windows.UI.Xaml.Media.h>
 #include "Model\Netif.h"
 
 namespace winrt::Maple_App::implementation
 {
+    using namespace std::chrono_literals;
+
     using namespace winrt::Windows::UI::ViewManagement;
+    using namespace winrt::Windows::UI::Xaml;
+    using namespace winrt::Windows::UI::Xaml::Media;
 
     std::string getNormalizedExtentionFromPath(const winrt::hstring& path) {
         auto ext = std::filesystem::path(std::wstring_view(path)).extension().string();
@@ -21,6 +26,34 @@ namespace winrt::Maple_App::implementation
     MainPage::MainPage()
     {
         InitializeComponent();
+
+        const auto coreTitleBar{ CoreApplication::GetCurrentView().TitleBar() };
+        const auto window{ Window::Current() };
+        coreTitleBar.ExtendViewIntoTitleBar(true);
+        window.SetTitleBar(CustomTitleBar());
+
+        coreTitleBar.LayoutMetricsChanged({ this,&MainPage::CoreTitleBar_LayoutMetricsChanged });
+        window.Activated({ this, &MainPage::CoreWindow_Activated });
+    }
+
+    void MainPage::CoreTitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar const& coreTitleBar, IInspectable const&)
+    {
+        LeftPaddingColumn().Width(GridLength{ .Value = coreTitleBar.SystemOverlayLeftInset(), .GridUnitType = GridUnitType::Pixel });
+        RightPaddingColumn().Width(GridLength{ .Value = coreTitleBar.SystemOverlayRightInset(), .GridUnitType = GridUnitType::Pixel });
+    }
+    void MainPage::CoreWindow_Activated(IInspectable const&, WindowActivatedEventArgs const& args)
+    {
+        UISettings settings{};
+        if (args.WindowActivationState() == CoreWindowActivationState::Deactivated)
+        {
+            AppTitleTextBlock().Foreground(
+                SolidColorBrush{ settings.UIElementColor(UIElementType::GrayText) });
+        }
+        else
+        {
+            AppTitleTextBlock().Foreground(
+                SolidColorBrush{ settings.GetColorValue(UIColorType::Foreground) });
+        }
     }
 
     DependencyProperty MainPage::ConfigItemsProperty()
@@ -50,6 +83,8 @@ namespace winrt::Maple_App::implementation
                 }
             }
             });
+
+        StartConnectionCheck();
     }
 
     IAsyncAction MainPage::NotifyUser(const hstring& message) {
@@ -276,7 +311,7 @@ namespace winrt::Maple_App::implementation
             boxed_netifs.reserve(netifs.size());
             std::transform(netifs.begin(), netifs.end(), std::back_inserter(boxed_netifs), [](const auto& netif) -> auto {
                 return netif;
-            });
+                });
             NetifCombobox().ItemsSource(single_threaded_vector(std::move(boxed_netifs)));
 
             const auto& currentNetif = ApplicationData::Current().LocalSettings().Values().TryLookup(NETIF_SETTING_KEY).try_as<hstring>();
@@ -380,8 +415,6 @@ namespace winrt::Maple_App::implementation
     fire_and_forget MainPage::GenerateProfileButton_Click(IInspectable const& sender, RoutedEventArgs const& e)
     {
         const auto lifetime = get_strong();
-        using namespace winrt::Windows::Networking::Vpn;
-        const auto& agent = VpnManagementAgent{};
         const auto& profile = VpnPlugInProfile{};
         profile.AlwaysOn(false);
         profile.ProfileName(L"Maple");
@@ -389,7 +422,7 @@ namespace winrt::Maple_App::implementation
         profile.VpnPluginPackageFamilyName(Windows::ApplicationModel::Package::Current().Id().FamilyName());
         profile.RememberCredentials(false);
         profile.ServerUris().Append(Uri{ L"https://github.com/YtFlow/Maple" });
-        const auto& result = co_await agent.AddProfileFromObjectAsync(profile);
+        const auto& result = co_await VpnMgmtAgent.AddProfileFromObjectAsync(profile);
         if (result == VpnManagementErrorStatus::Ok) {
             co_await NotifyUser(L"Profile generated.");
         }
@@ -397,5 +430,72 @@ namespace winrt::Maple_App::implementation
             co_await NotifyUser(L"Failed to generate a profile (" + to_hstring(static_cast<int32_t>(result)) + L").");
         }
     }
-}
 
+    fire_and_forget MainPage::ConnectionToggleSwitch_Toggled(IInspectable const&, RoutedEventArgs const&)
+    {
+        const auto lifetime{ get_strong() };
+
+        if (!ApplicationData::Current().LocalSettings().Values().HasKey(NETIF_SETTING_KEY)) {
+            MainPivot().SelectedIndex(1);
+            co_await 400ms;
+            co_await resume_foreground(Dispatcher());
+            NetifCombobox().IsDropDownOpen(true);
+            co_return;
+        }
+
+        const auto connect = ConnectionToggleSwitch().IsOn();
+        ConnectionToggleSwitch().IsEnabled(false);
+        VpnManagementErrorStatus status = VpnManagementErrorStatus::Ok;
+        if (connect) {
+            status = co_await VpnMgmtAgent.ConnectProfileAsync(m_vpnProfile);
+        }
+        else {
+            status = co_await VpnMgmtAgent.DisconnectProfileAsync(m_vpnProfile);
+        }
+        if (status == VpnManagementErrorStatus::Ok)
+        {
+            ConnectionToggleSwitch().IsEnabled(true);
+        }
+        else {
+            NotifyUser(L"Could not perform the requested operation. Please try again from system VPN settings for detailed error messages.");
+        }
+    }
+    fire_and_forget MainPage::StartConnectionCheck()
+    {
+        const auto lifetime{ get_strong() };
+        IVectorView<IVpnProfile> profiles{ nullptr };
+
+        auto event_token{ ConnectionToggleSwitch().Toggled({ this, &MainPage::ConnectionToggleSwitch_Toggled }) };
+        while (true) {
+            if (m_vpnProfile == nullptr) {
+                profiles = co_await VpnMgmtAgent.GetProfilesAsync();
+                for (auto const p : profiles) {
+                    if (p.ProfileName() == L"Maple" || p.ProfileName() == L"maple") {
+                        m_vpnProfile = p.try_as<VpnPlugInProfile>();
+                        break;
+                    }
+                }
+            }
+            if (m_vpnProfile == nullptr) {
+                ConnectionToggleSwitch().IsEnabled(false);
+            }
+            else {
+                ToolTipService::SetToolTip(ConnectionToggleSwitchContainer(), nullptr);
+                auto status = VpnManagementConnectionStatus::Disconnected;
+                try {
+                    status = m_vpnProfile.ConnectionStatus();
+                }
+                catch (...) {}
+
+                ConnectionToggleSwitch().IsEnabled(status == VpnManagementConnectionStatus::Connected
+                    || status == VpnManagementConnectionStatus::Disconnected);
+                ConnectionToggleSwitch().Toggled(event_token);
+                ConnectionToggleSwitch().IsOn(status == VpnManagementConnectionStatus::Connected
+                    || status == VpnManagementConnectionStatus::Connecting);
+                event_token = ConnectionToggleSwitch().Toggled({ this, &MainPage::ConnectionToggleSwitch_Toggled });
+            }
+            co_await 1s;
+            co_await resume_foreground(Dispatcher());
+        }
+    }
+}

+ 11 - 0
Maple.App/MainPage.h

@@ -3,15 +3,20 @@
 #include "MainPage.g.h"
 #include "Model/ConfigViewModel.h"
 
+#include <winrt/Windows.Networking.Vpn.h>
+
 using namespace winrt;
+using namespace Windows::ApplicationModel::Core;
 using namespace Windows::ApplicationModel::DataTransfer;
 using namespace Windows::Foundation;
 using namespace Windows::Foundation::Collections;
+using namespace Windows::Networking::Vpn;
 using namespace Windows::Storage;
 using namespace Windows::UI::Core;
 using namespace Windows::UI::Xaml;
 using namespace Windows::UI::Xaml::Controls;
 using namespace Windows::UI::Xaml::Input;
+using namespace Windows::UI::Xaml::Navigation;
 
 namespace winrt::Maple_App::implementation
 {
@@ -29,6 +34,8 @@ namespace winrt::Maple_App::implementation
         IObservableVector<Maple_App::ConfigViewModel> ConfigItems();
 
         void Page_Loaded(IInspectable const& sender, RoutedEventArgs const& e);
+        void CoreTitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar const& sender, IInspectable const& args);
+        void CoreWindow_Activated(IInspectable const& sender, WindowActivatedEventArgs const& args);
         void ConfigSetAsDefaultMenuItem_Click(IInspectable const& sender, RoutedEventArgs const& e);
         void ConfigRenameMenuItem_Click(IInspectable const& sender, RoutedEventArgs const& e);
         fire_and_forget ConfigDeleteMenuItem_Click(IInspectable const& sender, RoutedEventArgs const& e);
@@ -47,6 +54,7 @@ namespace winrt::Maple_App::implementation
         void WindowWidth_CurrentStateChanged(IInspectable const& sender, VisualStateChangedEventArgs const& e);
         void MainSplitView_PaneClosing(SplitView const& sender, SplitViewPaneClosingEventArgs const& args);
         fire_and_forget GenerateProfileButton_Click(IInspectable const& sender, RoutedEventArgs const& e);
+        fire_and_forget ConnectionToggleSwitch_Toggled(IInspectable const& sender, RoutedEventArgs const& e);
 
     private:
         inline static DependencyProperty m_configItemsProperty =
@@ -64,9 +72,11 @@ namespace winrt::Maple_App::implementation
                 nullptr
             );
         inline static SystemNavigationManager NavigationManager{ nullptr };
+        inline static VpnManagementAgent VpnMgmtAgent{};
 
         IStorageFolder m_configFolder{ nullptr };
         Maple_App::ConfigViewModel m_defaultConfig{ nullptr };
+        VpnPlugInProfile m_vpnProfile{ nullptr };
 
         static IAsyncAction NotifyUser(const hstring& message);
         static IAsyncOperation<IStorageFolder> InitializeConfigFolder();
@@ -77,6 +87,7 @@ namespace winrt::Maple_App::implementation
         void SetAsDefault(const Maple_App::ConfigViewModel& item);
         fire_and_forget ConfirmRename();
         IAsyncAction LoadConfigs();
+        fire_and_forget StartConnectionCheck();
 
         template<ConvertableToIStorageItem T>
         IAsyncAction ImportFiles(const IVectorView<T>& items) {

+ 213 - 167
Maple.App/MainPage.xaml

@@ -17,176 +17,222 @@
         <pickers:FileOpenPicker x:Key="ImportFilePicker" x:Name="ImportFilePicker"/>
     </Page.Resources>
 
-    <SplitView x:Name="MainSplitView" DisplayMode="Overlay" PaneClosing="MainSplitView_PaneClosing">
-        <SplitView.Pane>
-            <Pivot PivotItemLoaded="MainPivot_PivotItemLoaded">
-                <PivotItem Header="Config" Margin="0">
-                    <Grid>
-                        <Grid.RowDefinitions>
-                            <RowDefinition Height="*"/>
-                            <RowDefinition Height="Auto"/>
-                        </Grid.RowDefinitions>
-                        <ListView
-                            x:Name="ConfigListView"
-                            Grid.Row="0"
-                            SelectionMode="Single"
-                            CanDragItems="True"
-                            AllowDrop="True"
-                            ItemsSource="{x:Bind ConfigItems, Mode=OneWay}"
-                            SelectionChanged="ConfigListView_SelectionChanged"
-                            DragItemsStarting="ConfigListView_DragItemsStarting"
-                            DragOver="ConfigListView_DragOver"
-                            Drop="ConfigListView_Drop">
-                            <ListView.ItemContainerStyle>
-                                <Style TargetType="ListViewItem">
-                                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
-                                </Style>
-                            </ListView.ItemContainerStyle>
-                            <ListView.Resources>
-                                <MenuFlyout x:Key="ItemContextFlyout">
-                                    <MenuFlyoutItem Icon="Favorite" Text="Set as Default" Click="ConfigSetAsDefaultMenuItem_Click"/>
-                                    <MenuFlyoutItem Icon="Copy" Text="Duplicate" Click="ConfigDuplicateMenuItem_Click"/>
-                                    <MenuFlyoutItem Icon="Rename" Text="Rename" Click="ConfigRenameMenuItem_Click">
-                                        <Windows10FallCreatorsUpdate:MenuFlyoutItem.KeyboardAccelerators>
-                                            <Windows10FallCreatorsUpdate:KeyboardAccelerator Key="F2" ScopeOwner="{x:Bind ConfigListView}"/>
-                                        </Windows10FallCreatorsUpdate:MenuFlyoutItem.KeyboardAccelerators>
-                                    </MenuFlyoutItem>
-                                    <MenuFlyoutItem Icon="Delete" Text="Delete" Click="ConfigDeleteMenuItem_Click">
-                                        <MenuFlyoutItem.Command>
-                                            <Windows10version1809:StandardUICommand Kind="Delete"/>
-                                        </MenuFlyoutItem.Command>
-                                    </MenuFlyoutItem>
-                                </MenuFlyout>
-                            </ListView.Resources>
-                            <ListView.ItemTemplate>
-                                <DataTemplate x:DataType="maple_app:ConfigViewModel">
-                                    <Grid
-                                        HorizontalAlignment="Stretch"
-                                        VerticalAlignment="Stretch"
-                                        ContextFlyout="{StaticResource ItemContextFlyout}"
-                                        DoubleTapped="ConfigItem_DoubleTapped">
-                                        <Grid.RowDefinitions>
-                                            <RowDefinition Height="Auto"/>
-                                            <RowDefinition Height="Auto"/>
-                                        </Grid.RowDefinitions>
-                                        <Grid.ColumnDefinitions>
-                                            <ColumnDefinition Width="0"/>
-                                            <ColumnDefinition Width="*"/>
-                                        </Grid.ColumnDefinitions>
-                                        <Rectangle
-                                            Grid.Row="0"
-                                            Grid.RowSpan="2"
-                                            Grid.Column="0"
-                                            Margin="-12, 6, 4, 6"
-                                            Fill="{ThemeResource SystemAccentColor}"
-                                            Visibility="{x:Bind IsDefault, Mode=OneWay}"/>
-                                        <TextBlock
-                                            Grid.Row="0"
-                                            Grid.Column="1"
-                                            Text="{x:Bind Name, Mode=OneWay}"/>
-                                        <TextBlock
-                                            Grid.Row="1"
-                                            Grid.Column="1"
-                                            Text="{x:Bind DateUpdated, Mode=OneWay, Converter={StaticResource DateTimeConverter}}"
-                                            Style="{ThemeResource CaptionTextBlockStyle}"
-                                            Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}"/>
-                                    </Grid>
-                                </DataTemplate>
-                            </ListView.ItemTemplate>
-                        </ListView>
-                        <CommandBar Grid.Row="1">
-                            <AppBarButton Icon="Add" Label="Add">
-                                <AppBarButton.Flyout>
-                                    <MenuFlyout Placement="Top">
-                                        <MenuFlyoutItem Text="Conf" Click="ConfigCreateMenuItem_Click"/>
-                                        <MenuFlyoutItem Text="JSON" Click="ConfigCreateMenuItem_Click"/>
-                                        <MenuFlyoutSeparator/>
-                                        <MenuFlyoutItem Text="Import…" Click="ConfigImportMenuItem_Click"/>
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto" />
+            <RowDefinition />
+        </Grid.RowDefinitions>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>
+            <ColumnDefinition />
+            <ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>
+        </Grid.ColumnDefinitions>
+
+        <Grid Grid.Column="1" x:Name="CustomTitleBar" Height="32" Background="Transparent">
+            <TextBlock
+                x:Name="AppTitleTextBlock"
+                Text="Maple"
+                VerticalAlignment="Center"
+                FontSize="13"
+                Margin="12, 0, 0, 0"/>
+        </Grid>
+        <Grid
+            x:Name="ConnectionToggleSwitchContainer"
+            Grid.Column="1"
+            HorizontalAlignment="Right"
+            VerticalAlignment="Center"
+            Background="Transparent">
+            <ToolTipService.ToolTip>
+                <ToolTip x:Name="NoProfileToggleTooltip">
+                    Maple VPN profile is not ready. Please check Setting page for more information.
+                </ToolTip>
+            </ToolTipService.ToolTip>
+            <ToggleSwitch x:Name="ConnectionToggleSwitch" IsEnabled="False">
+                <ToggleSwitch.RenderTransform>
+                    <CompositeTransform ScaleX=".8" ScaleY=".8" TranslateX="90" TranslateY="3" />
+                </ToggleSwitch.RenderTransform>
+            </ToggleSwitch>
+        </Grid>
+
+        <SplitView
+            x:Name="MainSplitView"
+            RequestedTheme="Light"
+            Grid.Row="1"
+            Grid.ColumnSpan="3"
+            DisplayMode="Overlay"
+            PaneClosing="MainSplitView_PaneClosing"
+            Background="White" >
+            <SplitView.Pane>
+                <Pivot x:Name="MainPivot" PivotItemLoaded="MainPivot_PivotItemLoaded">
+                    <PivotItem Header="Config" Margin="0">
+                        <Grid>
+                            <Grid.RowDefinitions>
+                                <RowDefinition Height="*"/>
+                                <RowDefinition Height="Auto"/>
+                            </Grid.RowDefinitions>
+                            <ListView
+                                x:Name="ConfigListView"
+                                Grid.Row="0"
+                                SelectionMode="Single"
+                                CanDragItems="True"
+                                AllowDrop="True"
+                                ItemsSource="{x:Bind ConfigItems, Mode=OneWay}"
+                                SelectionChanged="ConfigListView_SelectionChanged"
+                                DragItemsStarting="ConfigListView_DragItemsStarting"
+                                DragOver="ConfigListView_DragOver"
+                                Drop="ConfigListView_Drop">
+                                <ListView.ItemContainerStyle>
+                                    <Style TargetType="ListViewItem">
+                                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
+                                    </Style>
+                                </ListView.ItemContainerStyle>
+                                <ListView.Resources>
+                                    <MenuFlyout x:Key="ItemContextFlyout">
+                                        <MenuFlyoutItem Icon="Favorite" Text="Set as Default" Click="ConfigSetAsDefaultMenuItem_Click"/>
+                                        <MenuFlyoutItem Icon="Copy" Text="Duplicate" Click="ConfigDuplicateMenuItem_Click"/>
+                                        <MenuFlyoutItem Icon="Rename" Text="Rename" Click="ConfigRenameMenuItem_Click">
+                                            <Windows10FallCreatorsUpdate:MenuFlyoutItem.KeyboardAccelerators>
+                                                <Windows10FallCreatorsUpdate:KeyboardAccelerator Key="F2" ScopeOwner="{x:Bind ConfigListView}"/>
+                                            </Windows10FallCreatorsUpdate:MenuFlyoutItem.KeyboardAccelerators>
+                                        </MenuFlyoutItem>
+                                        <MenuFlyoutItem Icon="Delete" Text="Delete" Click="ConfigDeleteMenuItem_Click">
+                                            <MenuFlyoutItem.Command>
+                                                <Windows10version1809:StandardUICommand Kind="Delete"/>
+                                            </MenuFlyoutItem.Command>
+                                        </MenuFlyoutItem>
                                     </MenuFlyout>
-                                </AppBarButton.Flyout>
-                            </AppBarButton>
-                        </CommandBar>
-                        <ContentDialog
-                            x:Name="RenameDialog"
-                            Height="10"
-                            Title="Specify a new file name"
-                            PrimaryButtonText="Rename"
-                            SecondaryButtonText="Close"
-                            PrimaryButtonClick="RenameDialogPrimaryButton_Click">
-                            <ContentDialog.SecondaryButtonCommand>
-                                <Windows10version1809:StandardUICommand Kind="Close"/>
-                            </ContentDialog.SecondaryButtonCommand>
-                            <TextBox
-                                x:Name="RenameDialogText"
-                                MaxLength="100"
-                                Height="32"
-                                AcceptsReturn="False"
-                                TextWrapping="NoWrap"
-                                KeyDown="RenameDialogText_KeyDown"/>
-                        </ContentDialog>
-                    </Grid>
-                </PivotItem>
-                <PivotItem Header="Setting">
-                    <ScrollViewer>
-                        <StackPanel>
-                            <TextBlock Margin="0, 18" Text="Network Interface" Style="{ThemeResource TitleTextBlockStyle}"/>
-                            <TextBlock
-                                Text="Choose a default network interface for DNS and outbound connections"
-                                TextWrapping="WrapWholeWords"/>
-                            <ComboBox
-                                x:Name="NetifCombobox"
-                                HorizontalAlignment="Stretch"
-                                Margin="0, 8"
-                                DisplayMemberPath="Desc"
-                                SelectedValuePath="Addr"
-                                SelectionChanged="NetifCombobox_SelectionChanged"/>
+                                </ListView.Resources>
+                                <ListView.ItemTemplate>
+                                    <DataTemplate x:DataType="maple_app:ConfigViewModel">
+                                        <Grid
+                                            HorizontalAlignment="Stretch"
+                                            VerticalAlignment="Stretch"
+                                            ContextFlyout="{StaticResource ItemContextFlyout}"
+                                            DoubleTapped="ConfigItem_DoubleTapped">
+                                            <Grid.RowDefinitions>
+                                                <RowDefinition Height="Auto"/>
+                                                <RowDefinition Height="Auto"/>
+                                            </Grid.RowDefinitions>
+                                            <Grid.ColumnDefinitions>
+                                                <ColumnDefinition Width="0"/>
+                                                <ColumnDefinition Width="*"/>
+                                            </Grid.ColumnDefinitions>
+                                            <Rectangle
+                                                Grid.Row="0"
+                                                Grid.RowSpan="2"
+                                                Grid.Column="0"
+                                                Margin="-12, 6, 4, 6"
+                                                Fill="{ThemeResource SystemAccentColor}"
+                                                Visibility="{x:Bind IsDefault, Mode=OneWay}"/>
+                                            <TextBlock
+                                                Grid.Row="0"
+                                                Grid.Column="1"
+                                                Text="{x:Bind Name, Mode=OneWay}"/>
+                                            <TextBlock
+                                                Grid.Row="1"
+                                                Grid.Column="1"
+                                                Text="{x:Bind DateUpdated, Mode=OneWay, Converter={StaticResource DateTimeConverter}}"
+                                                Style="{ThemeResource CaptionTextBlockStyle}"
+                                                Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}"/>
+                                        </Grid>
+                                    </DataTemplate>
+                                </ListView.ItemTemplate>
+                            </ListView>
+                            <CommandBar Grid.Row="1">
+                                <AppBarButton Icon="Add" Label="Add">
+                                    <AppBarButton.Flyout>
+                                        <MenuFlyout Placement="Top">
+                                            <MenuFlyoutItem Text="Conf" Click="ConfigCreateMenuItem_Click"/>
+                                            <MenuFlyoutItem Text="JSON" Click="ConfigCreateMenuItem_Click"/>
+                                            <MenuFlyoutSeparator/>
+                                            <MenuFlyoutItem Text="Import…" Click="ConfigImportMenuItem_Click"/>
+                                        </MenuFlyout>
+                                    </AppBarButton.Flyout>
+                                </AppBarButton>
+                            </CommandBar>
+                            <ContentDialog
+                                x:Name="RenameDialog"
+                                Height="10"
+                                Title="Specify a new file name"
+                                PrimaryButtonText="Rename"
+                                SecondaryButtonText="Close"
+                                PrimaryButtonClick="RenameDialogPrimaryButton_Click">
+                                <ContentDialog.SecondaryButtonCommand>
+                                    <Windows10version1809:StandardUICommand Kind="Close"/>
+                                </ContentDialog.SecondaryButtonCommand>
+                                <TextBox
+                                    x:Name="RenameDialogText"
+                                    MaxLength="100"
+                                    Height="32"
+                                    AcceptsReturn="False"
+                                    TextWrapping="NoWrap"
+                                    KeyDown="RenameDialogText_KeyDown"/>
+                            </ContentDialog>
+                        </Grid>
+                    </PivotItem>
+                    <PivotItem Header="Setting">
+                        <ScrollViewer>
+                            <StackPanel>
+                                <TextBlock Margin="0, 18" Text="Network Interface" Style="{ThemeResource TitleTextBlockStyle}"/>
+                                <TextBlock
+                                    Text="Choose a default network interface for DNS and outbound connections"
+                                    TextWrapping="WrapWholeWords"/>
+                                <ComboBox
+                                    x:Name="NetifCombobox"
+                                    HorizontalAlignment="Stretch"
+                                    Margin="0, 8"
+                                    DisplayMemberPath="Desc"
+                                    SelectedValuePath="Addr"
+                                    SelectionChanged="NetifCombobox_SelectionChanged"/>
+
+                                <TextBlock Margin="0, 18" Text="VPN Connection" Style="{ThemeResource TitleTextBlockStyle}"/>
+                                <TextBlock Margin="0, 0, 0, 18" TextWrapping="WrapWholeWords">
+                                    <Run Text="Connect to Maple in the"/>
+                                    <Hyperlink TextDecorations="None" NavigateUri="ms-settings:network-vpn">
+                                        <Run Text="Windows Settings"/>
+                                    </Hyperlink>
+                                    <Run Text="app."/>
+                                </TextBlock>
+                                <TextBlock
+                                    Margin="0, 0, 0, 8"
+                                    Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}"
+                                    TextWrapping="WrapWholeWords">
+                                    <Run Text="For first time connection, create a VPN profile with provider"/>
+                                    <Run FontFamily="Consolas" Text="Maple"/>
+                                    <Run Text="and server name"/>
+                                    <Run FontFamily="Consolas" Text="maple"/>
+                                    <Run Text=". Alternatively, select &quot;Generate Profile&quot; to create one automatically (not recommended)."/>
+                                </TextBlock>
+                                <Button Content="Generate Profile" Click="GenerateProfileButton_Click"/>
 
-                            <TextBlock Margin="0, 18" Text="VPN Connection" Style="{ThemeResource TitleTextBlockStyle}"/>
-                            <TextBlock Margin="0, 0, 0, 18" TextWrapping="WrapWholeWords">
-                                <Run Text="Connect to Maple in the"/>
-                                <Hyperlink TextDecorations="None" NavigateUri="ms-settings:network-vpn">
-                                    <Run Text="Windows Settings"/>
-                                </Hyperlink>
-                                <Run Text="app."/>
-                            </TextBlock>
-                            <TextBlock
-                                Margin="0, 0, 0, 8"
-                                Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}"
-                                TextWrapping="WrapWholeWords">
-                                <Run Text="For first time connection, create a VPN profile with provider"/>
-                                <Run FontFamily="Consolas" Text="Maple"/>
-                                <Run Text="and server name"/>
-                                <Run FontFamily="Consolas" Text="maple"/>
-                                <Run Text=". Alternatively, select &quot;Generate Profile&quot; to create one automatically."/>
-                            </TextBlock>
-                            <Button Content="Generate Profile" Click="GenerateProfileButton_Click"/>
+                                <TextBlock Margin="0, 18" Text="About" Style="{ThemeResource TitleTextBlockStyle}"/>
+                                <HyperlinkButton Padding="0" Content="Homepage" NavigateUri="https://github.com/YtFlow/Maple"/>
+                                <HyperlinkButton Padding="0" Content="Report Issues" NavigateUri="https://github.com/YtFlow/Maple/issues"/>
+                                <HyperlinkButton Padding="0" Content="License" NavigateUri="https://github.com/YtFlow/Maple/blob/main/LICENSE"/>
 
-                            <TextBlock Margin="0, 18" Text="About" Style="{ThemeResource TitleTextBlockStyle}"/>
-                            <HyperlinkButton Padding="0" Content="Homepage" NavigateUri="https://github.com/YtFlow/Maple"/>
-                            <HyperlinkButton Padding="0" Content="Report Issues" NavigateUri="https://github.com/YtFlow/Maple/issues"/>
-                            <HyperlinkButton Padding="0" Content="License" NavigateUri="https://github.com/YtFlow/Maple/blob/main/LICENSE"/>
+                                <TextBlock Margin="0, 20, 0, 0" TextWrapping="WrapWholeWords">
+                                    <Run>This product contains a</Run>
+                                    <Hyperlink NavigateUri="https://github.com/YtFlow/leaf" TextDecorations="None">
+                                        <Run>modified version</Run>
+                                    </Hyperlink>
+                                    <Run>of</Run>
+                                    <Hyperlink NavigateUri="https://github.com/eycorsican/leaf" TextDecorations="None">
+                                        <Run>eycorsican/leaf</Run>
+                                    </Hyperlink>
+                                    <Run>under</Run>
+                                    <Hyperlink NavigateUri="https://github.com/eycorsican/leaf/blob/master/LICENSE" TextDecorations="None">
+                                        <Run>Apache License 2.0</Run>
+                                    </Hyperlink>
+                                    <Run>.</Run>
+                                </TextBlock>
+                            </StackPanel>
+                        </ScrollViewer>
+                    </PivotItem>
+                </Pivot>
+            </SplitView.Pane>
+            <Frame x:Name="MainContentFrame" CacheSize="0" />
+        </SplitView>
 
-                            <TextBlock Margin="0, 20, 0, 0" TextWrapping="WrapWholeWords">
-                                <Run>This product contains a</Run>
-                                <Hyperlink NavigateUri="https://github.com/YtFlow/leaf" TextDecorations="None">
-                                    <Run>modified version</Run>
-                                </Hyperlink>
-                                <Run>of</Run>
-                                <Hyperlink NavigateUri="https://github.com/eycorsican/leaf" TextDecorations="None">
-                                    <Run>eycorsican/leaf</Run>
-                                </Hyperlink>
-                                <Run>under</Run>
-                                <Hyperlink NavigateUri="https://github.com/eycorsican/leaf/blob/master/LICENSE" TextDecorations="None">
-                                    <Run>Apache License 2.0</Run>
-                                </Hyperlink>
-                                <Run>.</Run>
-                            </TextBlock>
-                        </StackPanel>
-                    </ScrollViewer>
-                </PivotItem>
-            </Pivot>
-        </SplitView.Pane>
-        <Frame x:Name="MainContentFrame" CacheSize="0"/>
         <VisualStateManager.VisualStateGroups>
             <VisualStateGroup CurrentStateChanged="WindowWidth_CurrentStateChanged">
                 <VisualState>
@@ -200,5 +246,5 @@
                 </VisualState>
             </VisualStateGroup>
         </VisualStateManager.VisualStateGroups>
-    </SplitView>
+    </Grid>
 </Page>

+ 13 - 11
README.md

@@ -5,9 +5,9 @@ A lightweight Universal Windows proxy app based on https://github.com/eycorsican
 
 - Comes with Leaf core:
    - Domain name resolution with built-in DNS processor
-   - `tun`/`http`/`socks`/`trojan`/`ws` chainable inbounds
+   - `tun`/`shadowsocks`/`socks`/`trojan`/`ws` chainable inbounds
    - `direct`/`drop`/`tls`/`ws`/`h2`/`shadowsocks`/`vmess`/`trojan`/`socks` chainable outbounds
-   - `failover`/`tryall`/`random`/`retry` composed outbounds
+   - `failover`/`tryall`/`static` composed outbounds
    - `amux` multiplexing
    - Rule system based on IP, GeoIP and domain name
    - External rules from GeoIP database and V2Ray [Domain List Community](https://github.com/v2fly/domain-list-community)
@@ -37,7 +37,7 @@ Maple as a UWP app is distributed for sideloading only. When installed, it acts
 1. Launch Maple from the Start menu.
 2. Edit configuration. Refer to https://github.com/eycorsican/leaf/blob/master/README.zh.md for further explanation.
 3. Save the configuration file.
-4. If any `EXTERNAL` or `GEOIP` directive is used, drag external database files into `Config` area. V2Ray Domain List Community database can be fetched at https://github.com/v2ray/domain-list-community/releases/latest/download/dlc.dat . For GeoIP database, see [this script](https://github.com/eycorsican/ileaf/blob/main/misc/download_data.sh#L10) for inspiration.
+4. If any `EXTERNAL` or `GEOIP` directive is used, drag external database files into `Config` area. V2Ray Domain List Community database can be fetched at https://github.com/v2ray/domain-list-community/releases/latest/download/dlc.dat . For GeoIP database, please go to [MaxMind Developer Portal](https://dev.maxmind.com/geoip/geolite2-free-geolocation-data) and sign up for free download.
 5. Rename these databases accordingly (if applicable). By default, GeoIP database is `geo.mmdb` and V2Ray Domain List Community database is `site.dat`.
 6. Go to Setting page in Maple. Choose your network adapter such as `Ethernet` or `WLAN`.
 7. Launch [Windows Settings](	ms-settings:network-vpn) app.
@@ -50,29 +50,31 @@ Maple as a UWP app is distributed for sideloading only. When installed, it acts
 
 ### Connect
 
-1. Launch [Windows Settings](	ms-settings:network-vpn) app.
-2. In the VPN Settings, select **Maple**, and then Connect.  
+- Simply click the toggle button on the title bar, or
+- In Windows 11, select the battery, network, or volume icon to open the Quick Settings panel. Find **Maple** in VPN panel and connect, or
+- In Windows 10, select the Network  icon on the taskbar, and click Maple. In [Windows Settings](	ms-settings:network-vpn) app, select **Maple**, and then Connect.  
+
 *Note: Modifying the current configuration file while VPN is connected will take effect immediately.*
-3. Select Disconnect to disconnect.
 
 ## TODO
 
-- VPN lifecycle management on Maple UI
+- <del>VPN lifecycle management on Maple UI</del>
 - Better editing experience
 - Log collection (currently logs are sent to Visual Studio Output window for debugging only)
 - <del>`external` entries</del>
 - VPN On Demand
 - Configurable routing entries
+- IPv6 support
 
 ## Build
 
-To build Leaf and Maple, a Rust `nightly-x86_64-pc-windows-msvc` toolchain, Windows 10 SDK 10.0.19041 and Visual Studio 2019 with C++ Development Workflow are required. [C++/WinRT Visual Studio extension](https://marketplace.visualstudio.com/items?itemName=CppWinRTTeam.cppwinrt101804264) must be installed to generate Windows Metadata.
+To build Leaf and Maple, a Rust `nightly-x86_64-pc-windows-msvc` toolchain, Windows 10 SDK 10.0.22000 and Visual Studio 2022 with C++ Development Workflow are required. [C++/WinRT Visual Studio extension](https://marketplace.visualstudio.com/items?itemName=CppWinRTTeam.cppwinrt101804264) must be installed to generate Windows Metadata.
 
 1. **Recursively** clone this repository.
 2. Open a PowerShell Prompt.
-3. Change working directory to `leaf/leaf-ffi`.
-4. `cargo build -Z build-std=std,panic_abort --target x86_64-uwp-windows-msvc`.  
-   For Release builds, use `cargo build -Z build-std=std,panic_abort --target x86_64-uwp-windows-msvc --release`.  
+3. Change working directory to `leaf`.
+4. `cargo build -p leaf-ffi -Z build-std=std,panic_abort --target x86_64-uwp-windows-msvc`.  
+   For Release builds, use `cargo build -p leaf-ffi -Z build-std=std,panic_abort --target x86_64-uwp-windows-msvc --release`.  
    See also https://github.com/eycorsican/leaf#build .
 5. Open `Maple.sln` in Visual Studio.
 6. Build Solution.

BIN
image/screenshot-setting1.png