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

Feature: Added open in VS/VS Code to status bar #12645

Merged
merged 17 commits into from
Jun 19, 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
45 changes: 45 additions & 0 deletions src/Files.App/Actions/Open/OpenInVSAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using Files.App.Contexts;
using Files.App.Shell;

namespace Files.App.Actions
{
internal sealed class OpenInVSAction : ObservableObject, IAction
{
private readonly IContentPageContext _context;

private readonly bool _isVSInstalled;

public string Label { get; } = "OpenInVS".GetLocalizedResource();

public string Description { get; } = "OpenInVSDescription".GetLocalizedResource();

public bool IsExecutable =>
_isVSInstalled &&
!string.IsNullOrWhiteSpace(_context.SolutionFilePath);

public OpenInVSAction()
{
_context = Ioc.Default.GetRequiredService<IContentPageContext>();

_isVSInstalled = SoftwareHelpers.IsVSInstalled();
if (_isVSInstalled )
_context.PropertyChanged += Context_PropertyChanged;
}

private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(IContentPageContext.SolutionFilePath))
OnPropertyChanged(nameof(IsExecutable));
}

public Task ExecuteAsync()
{
Win32API.RunPowershellCommand($"start {_context.SolutionFilePath}", false);

return Task.CompletedTask;
}
}
}
45 changes: 45 additions & 0 deletions src/Files.App/Actions/Open/OpenInVSCodeAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using Files.App.Contexts;
using Files.App.Shell;

namespace Files.App.Actions
{
internal sealed class OpenInVSCodeAction : ObservableObject, IAction
{
private readonly IContentPageContext _context;

private readonly bool _isVSCodeInstalled;

public string Label { get; } = "OpenInVSCode".GetLocalizedResource();

public string Description { get; } = "OpenInVSCodeDescription".GetLocalizedResource();

public bool IsExecutable =>
_isVSCodeInstalled &&
_context.Folder is not null;

public OpenInVSCodeAction()
{
_context = Ioc.Default.GetRequiredService<IContentPageContext>();

_isVSCodeInstalled = SoftwareHelpers.IsVSCodeInstalled();
if (_isVSCodeInstalled)
_context.PropertyChanged += Context_PropertyChanged;
}

public Task ExecuteAsync()
{
Win32API.RunPowershellCommand($"code {_context.ShellPage?.FilesystemViewModel.WorkingDirectory}", false);

return Task.CompletedTask;
}

private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(IContentPageContext.Folder))
OnPropertyChanged(nameof(IsExecutable));
}
}
}
2 changes: 2 additions & 0 deletions src/Files.App/Commands/CommandCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ public enum CommandCodes
RotateRight,

// Open
OpenInVS,
OpenInVSCode,
OpenProperties,
OpenSettings,
OpenTerminal,
Expand Down
4 changes: 4 additions & 0 deletions src/Files.App/Commands/Manager/CommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public IRichCommand this[HotKey hotKey]
public IRichCommand OpenItem => commands[CommandCodes.OpenItem];
public IRichCommand OpenItemWithApplicationPicker => commands[CommandCodes.OpenItemWithApplicationPicker];
public IRichCommand OpenParentFolder => commands[CommandCodes.OpenParentFolder];
public IRichCommand OpenInVS => commands[CommandCodes.OpenInVS];
public IRichCommand OpenInVSCode => commands[CommandCodes.OpenInVSCode];
public IRichCommand OpenProperties => commands[CommandCodes.OpenProperties];
public IRichCommand OpenSettings => commands[CommandCodes.OpenSettings];
public IRichCommand OpenTerminal => commands[CommandCodes.OpenTerminal];
Expand Down Expand Up @@ -236,6 +238,8 @@ public CommandManager()
[CommandCodes.OpenItem] = new OpenItemAction(),
[CommandCodes.OpenItemWithApplicationPicker] = new OpenItemWithApplicationPickerAction(),
[CommandCodes.OpenParentFolder] = new OpenParentFolderAction(),
[CommandCodes.OpenInVS] = new OpenInVSAction(),
[CommandCodes.OpenInVSCode] = new OpenInVSCodeAction(),
[CommandCodes.OpenProperties] = new OpenPropertiesAction(),
[CommandCodes.OpenSettings] = new OpenSettingsAction(),
[CommandCodes.OpenTerminal] = new OpenTerminalAction(),
Expand Down
2 changes: 2 additions & 0 deletions src/Files.App/Commands/Manager/ICommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public interface ICommandManager : IEnumerable<IRichCommand>
IRichCommand RotateLeft { get; }
IRichCommand RotateRight { get; }

IRichCommand OpenInVS { get; }
IRichCommand OpenInVSCode { get; }
IRichCommand OpenProperties { get; }
IRichCommand OpenSettings { get; }
IRichCommand OpenTerminal { get; }
Expand Down
19 changes: 16 additions & 3 deletions src/Files.App/Contexts/ContentPage/ContentPageContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ internal class ContentPageContext : ObservableObject, IContentPageContext

public bool ShowSearchUnindexedItemsMessage => ShellPage is not null && ShellPage.InstanceViewModel.ShowSearchUnindexedItemsMessage;

public bool CanExecuteGitAction => ShellPage is not null && ShellPage.InstanceViewModel.IsGitRepository && !GitHelpers.IsExecutingGitAction;
public bool IsGitRepository => ShellPage is not null && ShellPage.InstanceViewModel.IsGitRepository;

public bool CanExecuteGitAction => IsGitRepository && !GitHelpers.IsExecutingGitAction;

public string? SolutionFilePath => ShellPage?.FilesystemViewModel.SolutionFilePath;

public ContentPageContext()
{
Expand Down Expand Up @@ -150,6 +154,7 @@ private void InstanceViewModel_PropertyChanged(object? sender, PropertyChangedEv
OnPropertyChanged(nameof(ShowSearchUnindexedItemsMessage));
break;
case nameof(CurrentInstanceViewModel.IsGitRepository):
OnPropertyChanged(nameof(IsGitRepository));
OnPropertyChanged(nameof(CanExecuteGitAction));
break;
}
Expand All @@ -175,8 +180,15 @@ private void ToolbarViewModel_PropertyChanged(object? sender, PropertyChangedEve

private void FilesystemViewModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName is nameof(ItemViewModel.CurrentFolder))
OnPropertyChanged(nameof(Folder));
switch (e.PropertyName)
{
case nameof(ItemViewModel.CurrentFolder):
OnPropertyChanged(nameof(Folder));
break;
case nameof(ItemViewModel.SolutionFilePath):
OnPropertyChanged(nameof(SolutionFilePath));
break;
}
}

private void Update()
Expand All @@ -194,6 +206,7 @@ private void Update()
OnPropertyChanged(nameof(IsMultiPaneEnabled));
OnPropertyChanged(nameof(IsMultiPaneActive));
OnPropertyChanged(nameof(ShowSearchUnindexedItemsMessage));
OnPropertyChanged(nameof(IsGitRepository));
OnPropertyChanged(nameof(CanExecuteGitAction));
}

Expand Down
3 changes: 3 additions & 0 deletions src/Files.App/Contexts/ContentPage/IContentPageContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public interface IContentPageContext : INotifyPropertyChanged

bool ShowSearchUnindexedItemsMessage { get; }

bool IsGitRepository { get; }
yaira2 marked this conversation as resolved.
Show resolved Hide resolved
bool CanExecuteGitAction { get; }

string? SolutionFilePath { get; }
}
}
25 changes: 24 additions & 1 deletion src/Files.App/Data/Models/ItemViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
using Files.App.Helpers.FileListCache;
using Files.App.Shell;
using Files.App.Storage.FtpStorage;
using Files.App.UserControls;
using Files.App.ViewModels.Previews;
using Files.Backend.Helpers;
using Files.Backend.Services;
using Files.Backend.Services.SizeProvider;
using Files.Backend.ViewModels.Dialogs;
Expand Down Expand Up @@ -76,6 +76,13 @@ public ListedItem? CurrentFolder
private set => SetProperty(ref currentFolder, value);
}

private string? _SolutionFilePath;
public string? SolutionFilePath
{
get => _SolutionFilePath;
private set => SetProperty(ref _SolutionFilePath, value);
}

public CollectionViewSource viewSource;

private FileSystemWatcher watcher;
Expand Down Expand Up @@ -1732,6 +1739,8 @@ await Task.Run(async () =>
}, defaultIconPairs: DefaultIcons);

filesAndFolders.AddRange(fileList);

await dispatcherQueue.EnqueueOrInvokeAsync(CheckForSolutionFile, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
await OrderFilesAndFoldersAsync();
await ApplyFilesAndFoldersChangesAsync();
});
Expand Down Expand Up @@ -1769,6 +1778,20 @@ private Task EnumFromStorageFolderAsync(string path, BaseStorageFolder? rootFold
}, cancellationToken);
}

private void CheckForSolutionFile()
{
for (int i = 0; i < filesAndFolders.Count; i++)
{
if (FileExtensionHelpers.HasExtension(filesAndFolders[i].FileExtension, ".sln"))
{
SolutionFilePath = filesAndFolders[i].ItemPath;
return;
}
}

SolutionFilePath = null;
ferrariofilippo marked this conversation as resolved.
Show resolved Hide resolved
}

private async Task<CloudDriveSyncStatus> CheckCloudDriveSyncStatusAsync(IStorageItem item)
{
int? syncStatus = null;
Expand Down
49 changes: 49 additions & 0 deletions src/Files.App/Helpers/SoftwareHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using Microsoft.Win32;

namespace Files.App.Helpers
{
internal static class SoftwareHelpers
{
public static bool IsVSCodeInstalled()
{
string registryKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall";

var key = Registry.CurrentUser.OpenSubKey(registryKey);
if (key is null)
return false;

string? displayName;

foreach (var subKey in key.GetSubKeyNames().Select(key.OpenSubKey))
{
displayName = subKey?.GetValue("DisplayName") as string;
if (!string.IsNullOrWhiteSpace(displayName) && displayName.StartsWith("Microsoft Visual Studio Code"))
{
key.Close();

return true;
}
}

key.Close();

return false;
}

public static bool IsVSInstalled()
{
string registryKey = @"SOFTWARE\Microsoft\VisualStudio";

var key = Registry.LocalMachine.OpenSubKey(registryKey);
if (key is null)
return false;

key.Close();

return true;
}
}
}
36 changes: 36 additions & 0 deletions src/Files.App/ResourceDictionaries/PathIcons.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -1948,6 +1948,42 @@
</Setter.Value>
</Setter>
</Style>

<Style x:Key="ColorIconOpen" TargetType="local:OpacityIcon">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Viewbox
Width="16"
Height="16"
Stretch="Fill">
<Grid Width="16" Height="16">
<Path
x:Name="Path1"
Data="M8 2C4.68629 2 2 4.68629 2 8C2 11.3137 4.68629 14 8 14C11.3137 14 14 11.3137 14 8C14 4.68629 11.3137 2 8 2Z"
Fill="{ThemeResource IconAltBrush}" />
<Path
x:Name="Path2"
Data="M8 2C4.68629 2 2 4.68629 2 8C2 11.3137 4.68629 14 8 14C11.3137 14 14 11.3137 14 8C14 4.68629 11.3137 2 8 2ZM1 8C1 4.13401 4.13401 1 8 1C11.866 1 15 4.13401 15 8C15 11.866 11.866 15 8 15C4.13401 15 1 11.866 1 8Z"
Fill="{ThemeResource IconBaseBrush}" />
<Path
x:Name="Path3"
Data="M10.9621 5.30861C10.938 5.25051 10.9026 5.19602 10.8557 5.14857C10.8543 5.14715 10.8528 5.14574 10.8514 5.14433C10.7611 5.05509 10.637 5 10.5 5H6.5C6.22386 5 6 5.22386 6 5.5C6 5.77614 6.22386 6 6.5 6H9.29289L5.14645 10.1464C4.95118 10.3417 4.95118 10.6583 5.14645 10.8536C5.34171 11.0488 5.65829 11.0488 5.85355 10.8536L10 6.70711V9.5C10 9.77614 10.2239 10 10.5 10C10.7761 10 11 9.77614 11 9.5V5.50035C11 5.49923 11 5.49812 11 5.497C10.9996 5.43287 10.987 5.3688 10.9621 5.30861Z"
Fill="{ThemeResource AccentFillColorDefaultBrush}" />
</Grid>

<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled" />
<VisualState x:Name="Selected" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
12 changes: 12 additions & 0 deletions src/Files.App/Strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -3314,4 +3314,16 @@
<data name="ManageBranches" xml:space="preserve">
<value>Manage branches</value>
</data>
<data name="OpenInVS" xml:space="preserve">
<value>Visual Studio</value>
</data>
<data name="OpenInVSDescription" xml:space="preserve">
<value>Open the current directory in Visual Studio</value>
</data>
<data name="OpenInVSCode" xml:space="preserve">
<value>VS Code</value>
</data>
<data name="OpenInVSCodeDescription" xml:space="preserve">
<value>Open the current directory in Visual Studio Code</value>
</data>
</root>
38 changes: 38 additions & 0 deletions src/Files.App/UserControls/StatusBarControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,44 @@
Orientation="Horizontal"
Spacing="4">

<!-- Open in VS Code Button -->
<Button
x:Name="OpenInVSCodeButton"
Height="24"
Padding="8,0,8,0"
VerticalAlignment="Center"
x:Load="{x:Bind converters:MultiBooleanConverter.AndNotConvert(Commands.OpenInVSCode.IsExecutable, Commands.OpenInVS.IsExecutable), Mode=OneWay}"
Background="Transparent"
BorderBrush="Transparent"
Command="{x:Bind Commands.OpenInVSCode}"
ToolTipService.ToolTip="{x:Bind Commands.OpenInVSCode.LabelWithHotKey, Mode=OneWay}">
<Button.Content>
<StackPanel Orientation="Horizontal" Spacing="8">
<usercontrols:OpacityIcon Style="{StaticResource ColorIconOpen}" />
<TextBlock Text="{x:Bind Commands.OpenInVSCode.Label, Mode=OneWay}" />
</StackPanel>
</Button.Content>
</Button>

<!-- Open in VS Button -->
<Button
x:Name="OpenInVSButton"
Height="24"
Padding="8,0,8,0"
VerticalAlignment="Center"
x:Load="{x:Bind Commands.OpenInVS.IsExecutable, Mode=OneWay}"
Background="Transparent"
BorderBrush="Transparent"
Command="{x:Bind Commands.OpenInVS}"
ToolTipService.ToolTip="{x:Bind Commands.OpenInVS.LabelWithHotKey, Mode=OneWay}">
<Button.Content>
<StackPanel Orientation="Horizontal" Spacing="8">
<usercontrols:OpacityIcon Style="{StaticResource ColorIconOpen}" />
<TextBlock Text="{x:Bind Commands.OpenInVS.Label, Mode=OneWay}" />
</StackPanel>
</Button.Content>
</Button>

<!-- Pull Button -->
<Button
x:Name="GitPullButton"
Expand Down