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

Introducing Windows Customization #2383

Merged
merged 4 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions DevHome.sln
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevSetupAgent.Test", "Hyper
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HyperVExtension.HostGuestCommunication", "HyperVExtension\src\HyperVExtension.HostGuestCommunication\HyperVExtension.HostGuestCommunication.csproj", "{D759CD66-494C-4A00-8075-8B65A9891349}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Customization", "Customization", "{623998FD-B0A6-4980-95D5-A5072301CA10}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHome.Customization", "tools\Customization\DevHome.Customization\DevHome.Customization.csproj", "{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -769,6 +773,22 @@ Global
{D759CD66-494C-4A00-8075-8B65A9891349}.Release|x64.Build.0 = Release|x64
{D759CD66-494C-4A00-8075-8B65A9891349}.Release|x86.ActiveCfg = Release|x86
{D759CD66-494C-4A00-8075-8B65A9891349}.Release|x86.Build.0 = Release|x86
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Debug|Any CPU.ActiveCfg = Debug|x64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Debug|Any CPU.Build.0 = Debug|x64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Debug|arm64.ActiveCfg = Debug|arm64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Debug|arm64.Build.0 = Debug|arm64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Debug|x64.ActiveCfg = Debug|x64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Debug|x64.Build.0 = Debug|x64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Debug|x86.ActiveCfg = Debug|x86
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Debug|x86.Build.0 = Debug|x86
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|Any CPU.ActiveCfg = Release|x64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|Any CPU.Build.0 = Release|x64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|arm64.ActiveCfg = Release|arm64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|arm64.Build.0 = Release|arm64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|x64.ActiveCfg = Release|x64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|x64.Build.0 = Release|x64
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|x86.ActiveCfg = Release|x86
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -816,6 +836,8 @@ Global
{F4095FD3-6A3F-490B-966D-E63059612EE6} = {3E3791DF-070D-4ADE-96E8-93D6FBD53953}
{0E05A442-BDC7-43D4-A000-F8C986826716} = {3E3791DF-070D-4ADE-96E8-93D6FBD53953}
{D759CD66-494C-4A00-8075-8B65A9891349} = {81AACED5-CFB5-47A6-AFD6-4625AADCFFA3}
{623998FD-B0A6-4980-95D5-A5072301CA10} = {A972EC5B-FC61-4964-A6FF-F9633EB75DFD}
{AF527EA4-6A24-4BD6-BC6E-A5863DC3489C} = {623998FD-B0A6-4980-95D5-A5072301CA10}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {030B5641-B206-46BB-BF71-36FF009088FA}
Expand Down
2 changes: 1 addition & 1 deletion common/DevHome.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<PackageReference Include="Microsoft.Windows.DevHome.SDK" Version="0.200.427" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240227000" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Internal.Windows.DevHome.Helpers" Version="1.0.20240304-x1959" />
<PackageReference Include="Microsoft.Internal.Windows.DevHome.Helpers" Version="1.0.20240311-x1907" />
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.4" />
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.8" />
<PackageReference Include="System.Management.Automation" Version="7.2.8" />
Expand Down
1 change: 1 addition & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Dev Home currently has the following tools:
- [Dashboard](./tools.md#dashboard-tool)
- [Setup flow](./tools.md#setup-flow-tool)
- Extensions
- [Windows customization](../tools/Customization/DevHome.Customization/Customization.md)

## Extensions

Expand Down
4 changes: 4 additions & 0 deletions src/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using DevHome.Common.Models;
using DevHome.Common.Services;
using DevHome.Contracts.Services;
using DevHome.Customization.Extensions;
using DevHome.Dashboard.Extensions;
using DevHome.ExtensionLibrary.Extensions;
using DevHome.Helpers;
Expand Down Expand Up @@ -141,6 +142,9 @@ public App()

// Environments
services.AddEnvironments(context);

// Windows customization
services.AddWindowsCustomization(context);
}).
Build();

Expand Down
3 changes: 2 additions & 1 deletion src/DevHome.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Internal.Windows.DevHome.Helpers" Version="1.0.20240304-x1959" />
<PackageReference Include="Microsoft.Internal.Windows.DevHome.Helpers" Version="1.0.20240311-x1907" />
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta">
<PrivateAssets>all</PrivateAssets>
Expand All @@ -85,6 +85,7 @@
<ProjectReference Include="..\tools\ExtensionLibrary\DevHome.ExtensionLibrary\DevHome.ExtensionLibrary.csproj" />
<ProjectReference Include="..\HyperVExtension\src\HyperVExtensionServer\HyperVExtensionServer.csproj" />
<ProjectReference Include="..\tools\Environments\DevHome.Environments\DevHome.Environments.csproj" />
<ProjectReference Include="..\tools\Customization\DevHome.Customization\DevHome.Customization.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
7 changes: 7 additions & 0 deletions src/NavConfig.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@
"viewModelFullName": "DevHome.QuietBackgroundProcesses.UI.ViewModels.QuietBackgroundProcessesViewModel",
"icon": "f5b0",
"experimentalFeatureIdentity": "QuietBackgroundProcessesExperiment"
},
{
"identity": "DevHome.Customization",
"assembly": "DevHome.Customization",
"viewFullName": "DevHome.Customization.Views.MainPage",
"viewModelFullName": "DevHome.Customization.ViewModels.MainPageViewModel",
"icon": "e9f5"
}
]
}
Expand Down
2 changes: 2 additions & 0 deletions src/Services/PageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using DevHome.Common.Contracts;
using DevHome.Common.Models;
using DevHome.Common.Services;
using DevHome.Customization.Extensions;
using DevHome.ExtensionLibrary.Extensions;
using DevHome.Settings.Extensions;
using DevHome.ViewModels;
Expand Down Expand Up @@ -46,6 +47,7 @@ where assembly.GetName().Name == tool.Assembly

this.ConfigureExtensionLibraryPages();
this.ConfigureSettingsPages();
this.ConfigureCustomizationPages();

// Configure Experimental Feature pages
ExperimentalFeature.LocalSettingsService = localSettingsService;
Expand Down
13 changes: 11 additions & 2 deletions src/Styles/TextBlock.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,22 @@

<!-- Style (inc. the correct spacing) of a section header -->
<Style x:Key="SettingsSectionHeaderTextBlockStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock">
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock">
<Style.Setters>
<Setter Property="Margin" Value="1,28,0,4" />
</Style.Setters>
</Style>

<Style
x:Key="SettingsSectionCaptionTextBlockStyle"
BasedOn="{StaticResource CaptionTextBlockStyle}"
TargetType="TextBlock">
<Style.Setters>
<Setter Property="Margin" Value="1,0,0,8" />
krschau marked this conversation as resolved.
Show resolved Hide resolved
</Style.Setters>
</Style>

<Style x:Key="WidgetConfigHeaderTextStyle" TargetType="TextBlock">
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="FontSize" Value="{StaticResource LargeFontSize}" />
Expand Down
36 changes: 36 additions & 0 deletions tools/Customization/DevHome.Customization/Customization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Windows Customization

This directory contains the Windows Customization features for the DevHome project.

## Overview

The `DevHome.Customization` directory includes various features and settings that allow users to customize their Windows experience.


## Contributions

Windows Customization follows an MVVM (Model-View-ViewModel) architecture. The landing page, MainPage, is backed by a primary view, MainPageView. This serves as the first-level page for the overall Windows Customization feature set in DevHome, providing users with the ability to configure Windows settings to enhance their developer experience. The goal of this architecture is to ensure adaptability of the user interface.

MainPage hosts links to other pages via SettingsCard controls. These pages contain logically grouped settings and features that can be configured by the user. Each grouping is designed as portable controls, such as a UserControl. This design allows them to be hosted on the main page, included in a future "All Settings" page, and be part of a future filtered search results page.

New feature additions to should expect to implement the following:

1. **View**: Generally a StackPanel that contains all the user interface for the new feature or setting(s). This View should generally be implemented as a UserControl that can be hosted in any number of Pages.
- e.g. [DeveloperFileExplorerView](Views\DeveloperFileExplorerView.xaml)

1. **ViewModel**: Generally the implementation of data binding and commands needed to support the View, including notifications to trigger changes in the View's UI.
- e.g. [DeveloperFileExplorerViewModel](ViewModels\DeveloperFileExplorerViewModel.cs)

1. **Page**: Optionally include a page that hosts the View as a navigation target from the [MainPage](Views\MainPage.xaml). This may not be needed if the settings are minimal and can be hosted on the [MainPageView](Views\MainPageView.xaml) (e.g., as a SettingsExpander).
- e.g. [DeveloperFileExplorerPage](Views\DeveloperFileExplorerPage.xaml)

1. **Model**: Any implementation details for the setting or feature, i.e. the data model and any business or validation logic.
- e.g. [DeveloperFileExplorerSettings](Models\DeveloperFileExplorerSettings.cs)

1. **Service entries**: Pages, ViewModels, and any other services participating in dependency injection should be placed in AddWindowsCustomization in [ServiceExtensions.cs](Extensions\ServiceExtensions.cs)

1. **Strings**: Localized strings should leverage [Resources.resw](Strings\en-us\Resources.resw)

1. **Telemetry Events**: Any user interaction that results in a settings change should use [SettingChangedEvent](TelemetryEvents\SettingChangedEvent.cs)

> **Note**: Private APIs (including registry keys or values that are not documented) cannot be referenced in the Windows Customization project. For public contributions that involve a private API, please file an issue on GitHub to discuss options.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)ToolingVersions.props" />
<PropertyGroup>
<RootNamespace>DevHome.Customization</RootNamespace>
<Platforms>x86;x64;arm64</Platforms>
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
<Nullable>enable</Nullable>
<UseWinUI>true</UseWinUI>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\common\DevHome.Common.csproj" />
</ItemGroup>

<ItemGroup>
<AdditionalFiles Include="NativeMethods.txt" />
</ItemGroup>

<ItemGroup>
<None Remove="Views\DeveloperFileExplorerPage.xaml" />
<None Remove="Views\DeveloperFileExplorerView.xaml" />
<None Remove="Views\MainPage.xaml" />
<None Remove="Views\MainPageView.xaml" />
</ItemGroup>

<ItemGroup>
<Page Update="Views\DeveloperFileExplorerPage.xaml">
<Generator>MSBuild:Compile</Generator>
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\DeveloperFileExplorerView.xaml">
<Generator>MSBuild:Compile</Generator>
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Views\MainPageView.xaml">
<Generator>MSBuild:Compile</Generator>
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
</ItemGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DefineConstants>$(DefineConstants);DEBUG</DefineConstants>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using DevHome.Common.Services;
using DevHome.Customization.ViewModels;
using DevHome.Customization.Views;

namespace DevHome.Customization.Extensions;

public static class PageExtensions
{
public static void ConfigureCustomizationPages(this IPageService pageService)
{
pageService.Configure<DeveloperFileExplorerViewModel, DeveloperFileExplorerPage>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using DevHome.Customization.ViewModels;
using DevHome.Customization.Views;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace DevHome.Customization.Extensions;

public static class ServiceExtensions
{
public static IServiceCollection AddWindowsCustomization(this IServiceCollection services, HostBuilderContext context)
{
services.AddSingleton<MainPageViewModel>();
services.AddTransient<MainPage>();

services.AddSingleton<DeveloperFileExplorerViewModel>();
services.AddTransient<DeveloperFileExplorerPage>();

return services;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using Windows.Win32;
using Windows.Win32.UI.Shell;

namespace DevHome.Customization.Models;

internal static class DeveloperFileExplorerSettings
{
public static bool ShowFileExtensionsEnabled()
{
return GetShellSettings(SSF_MASK.SSF_SHOWEXTENSIONS);
}

public static void SetShowFileExtensionsEnabled(bool value)
{
SetShellSettings(SSF_MASK.SSF_SHOWEXTENSIONS, value);
}

public static bool ShowHiddenAndSystemFilesEnabled()
{
return GetShellSettings(SSF_MASK.SSF_SHOWALLOBJECTS);
}

public static void SetShowHiddenAndSystemFilesEnabled(bool value)
{
SetShellSettings(SSF_MASK.SSF_SHOWALLOBJECTS, value);
}

public static bool ShowFullPathInTitleBarEnabled()
{
return GetCabineteState(CabinetStateFlags.FullPathTitle);
}

public static void SetShowFullPathInTitleBarEnabled(bool value)
{
SetCabinetState(CabinetStateFlags.FullPathTitle, value);
}

private static bool GetShellSettings(SSF_MASK mask)
{
unsafe
{
var state = default(SHELLSTATEA);
PInvoke.SHGetSetSettings(&state, mask, false);
return (state._bitfield1 & (int)mask) != 0;
}
}

private static void SetShellSettings(SSF_MASK mask, bool value)
{
unsafe
{
var state = default(SHELLSTATEA);
PInvoke.SHGetSetSettings(&state, mask, false);
if (value)
{
state._bitfield1 |= (int)mask;
}
else
{
state._bitfield1 &= ~(int)mask;
}

PInvoke.SHGetSetSettings(&state, mask, true);
}
}

[Flags]
private enum CabinetStateFlags
{
FullPathTitle = 0x00000001,
}

private static bool GetCabineteState(CabinetStateFlags flags)
{
unsafe
{
var state = default(CABINETSTATE);
PInvoke.ReadCabinetState(&state, sizeof(CABINETSTATE));
return (state._bitfield & (int)flags) != 0;
}
}

private static void SetCabinetState(CabinetStateFlags flags, bool value)
{
unsafe
{
var state = default(CABINETSTATE);
PInvoke.ReadCabinetState(&state, sizeof(CABINETSTATE));
if (value)
{
state._bitfield |= (int)flags;
}
else
{
state._bitfield &= ~(int)flags;
}

PInvoke.WriteCabinetState(&state);
}
}
}
3 changes: 3 additions & 0 deletions tools/Customization/DevHome.Customization/NativeMethods.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SHGetSetSettings
ReadCabinetState
WriteCabinetState
Loading
Loading