Skip to content

Feature: Display "shield overlay" indicating when elevation is required #12068

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

Merged
merged 10 commits into from
Apr 16, 2023
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
1 change: 1 addition & 0 deletions src/Files.App/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public static class ImageRes
public const int OneDrive = 1043;
public const int Libraries = 1023;
public const int Folder = 3;
public const int ShieldIcon = 78;
}

public static class Shell32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ public async Task LoadDriveIcon()
if (!string.IsNullOrEmpty(DeviceID) && !string.Equals(DeviceID, "network-folder"))
IconData = await FileThumbnailHelper.LoadIconWithoutOverlayAsync(DeviceID, 24);

IconData ??= UIHelpers.GetIconResourceInfo(Constants.ImageRes.Folder).IconData;
IconData ??= UIHelpers.GetSidebarIconResourceInfo(Constants.ImageRes.Folder).IconData;
}

Icon = await IconData.ToBitmapAsync();
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/DataModels/SidebarPinnedModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public async Task<LocationItem> CreateLocationItemFromPathAsync(string path)
}
else
{
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueAsync(() => UIHelpers.GetIconResource(Constants.ImageRes.Folder));
locationItem.Icon = await App.Window.DispatcherQueue.EnqueueAsync(() => UIHelpers.GetSidebarIconResource(Constants.ImageRes.Folder));
locationItem.IsInvalid = true;
Debug.WriteLine($"Pinned item was invalid {res.ErrorCode}, item: {path}");
}
Expand Down
21 changes: 21 additions & 0 deletions src/Files.App/Filesystem/ListedItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,19 @@ public BitmapImage PlaceholderDefaultIcon
set => SetProperty(ref placeholderDefaultIcon, value);
}

private BitmapImage shieldIcon;
public BitmapImage ShieldIcon
{
get => shieldIcon;
set
{
if (value is not null)
{
SetProperty(ref shieldIcon, value);
}
}
}

private string itemPath;
public string ItemPath
{
Expand Down Expand Up @@ -421,6 +434,7 @@ public override string ToString()
public virtual bool IsExecutable => FileExtensionHelpers.IsExecutableFile(ItemPath);
public bool IsPinned => App.QuickAccessManager.Model.FavoriteItems.Contains(itemPath);
public bool IsDriveRoot => ItemPath == PathNormalization.GetPathRoot(ItemPath);
public bool IsElevated => CheckElevationRights();

private BaseStorageFile itemFile;
public BaseStorageFile ItemFile
Expand Down Expand Up @@ -449,6 +463,13 @@ public void SetDefaultIcon(BitmapImage img)
LoadDefaultIcon = true;
PlaceholderDefaultIcon = img;
}

private bool CheckElevationRights()
{
return IsShortcut
? ElevationHelpers.IsElevationRequired(((ShortcutItem)this).TargetPath)
: ElevationHelpers.IsElevationRequired(this.ItemPath);
}
}

public class RecycleBinItem : ListedItem
Expand Down
20 changes: 20 additions & 0 deletions src/Files.App/Helpers/ElevationHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.UI.Xaml.Media.Imaging;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace Files.App.Helpers
{
public static class ElevationHelpers
{
[DllImport("shell32.dll", EntryPoint = "#865", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)] private static extern bool _IsElevationRequired([MarshalAs(UnmanagedType.LPWStr)] string pszPath);

public static bool IsElevationRequired(string filePath)
{
if (string.IsNullOrEmpty(filePath))
return false;

return _IsElevationRequired(filePath);
}
}
}
38 changes: 28 additions & 10 deletions src/Files.App/Helpers/UIHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,22 +138,30 @@ public static void CloseAllDialogs()
}
}

private static IEnumerable<IconFileInfo> IconResources = UIHelpers.LoadSidebarIconResources();
private static IEnumerable<IconFileInfo> SidebarIconResources = LoadSidebarIconResources();

public static IconFileInfo GetIconResourceInfo(int index)
private static IconFileInfo ShieldIconResource = LoadShieldIconResource();

public static IconFileInfo GetSidebarIconResourceInfo(int index)
{
var icons = UIHelpers.IconResources;
var icons = UIHelpers.SidebarIconResources;
return icons is not null ? icons.FirstOrDefault(x => x.Index == index) : null;
}

public static async Task<BitmapImage> GetIconResource(int index)
public static async Task<BitmapImage?> GetSidebarIconResource(int index)
{
var iconInfo = GetIconResourceInfo(index);
if (iconInfo is not null)
{
return await iconInfo.IconData.ToBitmapAsync();
}
return null;
var iconInfo = GetSidebarIconResourceInfo(index);

return iconInfo is not null
? await iconInfo.IconData.ToBitmapAsync()
: null;
}

public static async Task<BitmapImage?> GetShieldIconResource()
{
return ShieldIconResource is not null
? await ShieldIconResource.IconData.ToBitmapAsync()
: null;
}

private static IEnumerable<IconFileInfo> LoadSidebarIconResources()
Expand All @@ -170,5 +178,15 @@ private static IEnumerable<IconFileInfo> LoadSidebarIconResources()

return imageResList;
}

private static IconFileInfo LoadShieldIconResource()
{
string imageres = Path.Combine(CommonPaths.SystemRootPath, "System32", "imageres.dll");
var imageResList = Win32API.ExtractSelectedIconsFromDLL(imageres, new List<int>() {
Constants.ImageRes.ShieldIcon
}, 16);

return imageResList.First();
}
}
}
15 changes: 14 additions & 1 deletion src/Files.App/ViewModels/ItemViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ public ListedItem CurrentFolder

private FileSystemWatcher watcher;

private static BitmapImage shieldIcon;

private CancellationTokenSource addFilesCTS;
private CancellationTokenSource semaphoreCTS;
private CancellationTokenSource loadPropsCTS;
Expand Down Expand Up @@ -837,7 +839,7 @@ public void UpdateGroupOptions()
FilesAndFolders.GetExtendedGroupHeaderInfo = groupInfoSelector.Item2;
}

public Dictionary<string, BitmapImage> DefaultIcons = new();
public Dictionary<string, BitmapImage> DefaultIcons = new ();

private uint currentDefaultIconSize = 0;

Expand Down Expand Up @@ -867,6 +869,13 @@ public bool IsLoadingItems
set => isLoadingItems = value;
}

private async Task<BitmapImage> GetShieldIcon()
{
shieldIcon ??= await UIHelpers.GetShieldIconResource();

return shieldIcon;
}

// ThumbnailSize is set to 96 so that unless we override it, mode is in turn set to SingleItem
private async Task LoadItemThumbnail(ListedItem item, uint thumbnailSize = 96, IStorageItem? matchingStorageItem = null)
{
Expand Down Expand Up @@ -910,6 +919,7 @@ await dispatcherQueue.EnqueueAsync(async () =>
await dispatcherQueue.EnqueueAsync(async () =>
{
item.IconOverlay = await overlayInfo.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}
Expand Down Expand Up @@ -937,6 +947,7 @@ await dispatcherQueue.EnqueueAsync(async () =>
await dispatcherQueue.EnqueueAsync(async () =>
{
item.IconOverlay = await iconInfo.OverlayData.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}
Expand Down Expand Up @@ -972,6 +983,7 @@ await dispatcherQueue.EnqueueAsync(async () =>
await dispatcherQueue.EnqueueAsync(async () =>
{
item.IconOverlay = await overlayInfo.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}
Expand All @@ -993,6 +1005,7 @@ await dispatcherQueue.EnqueueAsync(async () =>
await dispatcherQueue.EnqueueAsync(async () =>
{
item.IconOverlay = await iconInfo.OverlayData.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/ViewModels/SidebarViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ private async Task<LocationItem> CreateSection(SectionType sectionType)

if (iconIdex != -1)
{
section.Icon = await UIHelpers.GetIconResource(iconIdex);
section.Icon = await UIHelpers.GetSidebarIconResource(iconIdex);
}
}

Expand Down
28 changes: 19 additions & 9 deletions src/Files.App/Views/LayoutModes/ColumnViewBase.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
Canvas.ZIndex="0"
EmptyTextType="{x:Bind ParentShellPageInstance.FilesystemViewModel.EmptyTextType, Mode=OneWay}" />

<!-- Invalid Item Name Tip -->
<!-- Invalid Item Name Tip -->
<TeachingTip
x:Name="FileNameTeachingTip"
Grid.RowSpan="2"
Expand All @@ -149,7 +149,7 @@
ViewChangeStarted="SemanticZoom_ViewChangeStarted"
Visibility="{x:Bind FolderSettings.IsLayoutModeChanging, Mode=OneWay, Converter={StaticResource NegatedBoolToVisibilityConverter}}">

<!-- Main View -->
<!-- Main View -->
<SemanticZoom.ZoomedInView>
<ListView
x:Name="FileList"
Expand Down Expand Up @@ -199,7 +199,7 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<!-- Thumbnail -->
<!-- Thumbnail -->
<Grid
Grid.Column="0"
Width="24"
Expand Down Expand Up @@ -262,9 +262,19 @@
Visibility="{x:Bind IsShortcut}">
<uc:OpacityIcon Style="{StaticResource ColorIconShortcut}" />
</Border>
<Image
x:Name="ShieldOverlay"
Width="8"
Height="8"
Margin="2"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
x:Load="{x:Bind IsElevated}"
Source="{x:Bind ShieldIcon, Mode=OneWay}"
Stretch="Uniform" />
</Grid>

<!-- Item Name -->
<!-- Item Name -->
<TextBlock
x:Name="ItemName"
Grid.Column="1"
Expand All @@ -290,7 +300,7 @@
<Grid Grid.Column="2">
<StackPanel Orientation="Horizontal" Spacing="4">

<!-- Cloud Status -->
<!-- Cloud Status -->
<FontIcon
x:Name="CloudDriveSyncStatusGlyph"
HorizontalAlignment="Center"
Expand Down Expand Up @@ -391,7 +401,7 @@
</ListView>
</SemanticZoom.ZoomedInView>

<!-- Grouped View -->
<!-- Grouped View -->
<SemanticZoom.ZoomedOutView>
<ListView
HorizontalAlignment="Stretch"
Expand Down Expand Up @@ -427,7 +437,7 @@
Style="{StaticResource SubtitleTextBlockStyle}"
Text="{x:Bind ((helpers:IGroupedCollectionHeader)Group).Model.Text, Mode=OneWay}" />

<!-- Count -->
<!-- Count -->
<TextBlock
Margin="4,0,0,0"
VerticalAlignment="Center"
Expand All @@ -436,7 +446,7 @@
Text="{x:Bind ((helpers:IGroupedCollectionHeader)Group).Model.CountText, Mode=OneWay}"
Visibility="{x:Bind ((helpers:IGroupedCollectionHeader)Group).Model.ShowCountTextBelow, Mode=OneWay, Converter={StaticResource NegatedBoolToVisibilityConverter}}" />

<!-- Subheader -->
<!-- Subheader -->
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Expand All @@ -463,7 +473,7 @@

</SemanticZoom>

<!-- Selector -->
<!-- Selector -->
<Canvas>
<Rectangle
Name="SelectionRectangle"
Expand Down
9 changes: 9 additions & 0 deletions src/Files.App/Views/LayoutModes/DetailsLayoutBrowser.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,15 @@
x:Load="{x:Bind IsShortcut}">
<uc:OpacityIcon Style="{StaticResource ColorIconShortcut}" />
</Border>
<Image
x:Name="ShieldOverlay"
Width="8"
Height="8"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
x:Load="{x:Bind IsElevated}"
Source="{x:Bind ShieldIcon, Mode=OneWay}"
Stretch="Uniform" />
</Grid>
</Grid>

Expand Down
21 changes: 21 additions & 0 deletions src/Files.App/Views/LayoutModes/GridViewBrowser.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,17 @@
<uc:OpacityIcon Style="{StaticResource ColorIconShortcut}" />
</Viewbox>

<Image
x:Name="ShieldOverlay"
Width="16"
Height="16"
Margin="14"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
x:Load="{x:Bind IsElevated}"
x:Phase="1"
Source="{x:Bind ShieldIcon, Mode=OneWay}"
Stretch="Uniform" />
</Grid>

<Grid
Expand Down Expand Up @@ -371,6 +382,16 @@
<uc:OpacityIcon Style="{StaticResource ColorIconShortcut}" />
</Viewbox>

<Image
x:Name="ShieldOverlay"
Width="16"
Height="16"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
x:Load="{x:Bind IsElevated}"
x:Phase="1"
Source="{x:Bind ShieldIcon, Mode=OneWay}"
Stretch="Uniform" />
</Grid>

<Grid
Expand Down