-
Notifications
You must be signed in to change notification settings - Fork 337
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
Enable the user to unpin/pin an external tool on the bar #2972
Conversation
@@ -36,65 +68,161 @@ private ExternalToolsHelper() | |||
localFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location) ?? string.Empty; | |||
} | |||
|
|||
// The file should be in this location: | |||
// %LocalAppData%\Packages\Microsoft.Windows.DevHome.Dev_8wekyb3d8bbwe\LocalState\externaltools.json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest not using Dev package path
// %LocalAppData%\Packages\Microsoft.Windows.DevHome.Dev_8wekyb3d8bbwe\LocalState\externaltools.json | |
// %LocalAppData%\Packages\Microsoft.Windows.DevHome_8wekyb3d8bbwe\LocalState\externaltools.json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2nd commit.
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
// TODO If we failed parsing the JSON file... should we just delete it? | ||
// TODO If we failed to parse the JSON file, we should rename it (using DateTime.Now), | ||
// create a new one, and report to the user. | ||
_log.Error(ex, "Failed to parse {tool}", toolInfoFileName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_log.Error(ex, "Failed to parse {tool}", toolInfoFileName); | |
_log.Error(ex, $"Failed to parse {toolInfoFileName}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2nd commit.
private void ToolItem_PropertyChanged(object? sender, PropertyChangedEventArgs e) | ||
{ | ||
// The user can change the IsPinned property of a tool, to pin or unpin it on the bar. | ||
if (sender is ExternalTool tool && e.PropertyName == nameof(ExternalTool.IsPinned)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer string.Equals to ==
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2nd commit.
Grid.Row="4" Margin="0,12,0,0" HorizontalAlignment="Left" IsOn="True"/> | ||
|
||
<StackPanel Orientation="Horizontal" Grid.Row="5" HorizontalAlignment="Right" Margin="0,12,0,0"> | ||
<Button x:Uid="AddToolsOkButton" x:Name="OkButton" Click="OkButton_Click" Width="100"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use MinWidth, otherwise you have a potential text scaling (or long language) accessibility bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2nd commit.
Grid.Row="4" Margin="0,12,0,0" HorizontalAlignment="Left" IsOn="True"/> | ||
|
||
<StackPanel Orientation="Horizontal" Grid.Row="5" HorizontalAlignment="Right" Margin="0,12,0,0"> | ||
<Button x:Uid="AddToolsOkButton" x:Name="OkButton" Click="OkButton_Click" Width="100"/> | ||
<Button x:Uid="AddToolsCancelButton" x:Name="CancelButton" Margin="6,0,0,0" Width="100"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest adding Spacing to the surrounding StackPanel instead of Margin to the Button.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2nd commit.
@@ -292,7 +294,7 @@ private void CreateMenuItemFromTool(ExternalTool item) | |||
|
|||
private void ExternalToolItem_PropertyChanged(object? sender, PropertyChangedEventArgs e) | |||
{ | |||
if (sender is ExternalTool item && e.PropertyName == nameof(ExternalTool.MenuIcon)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it seems, and yes I fixed this one in the 2nd commit.
} | ||
|
||
// The bar shows only the pinned tools. | ||
public ObservableCollection<ExternalTool> FilteredExternalTools { get; private set; } = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I thought about wrapping it, but the set is private, and I need to be able to change at least the IsPinned property on any of the items in the collection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ReadonlyCollection only prevents you from adding/removing from the collection, not modifying the items in the collection. I think you want this as a ReadOnly collection, as you don't want other components adding/removing from this collection, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 3rd commit.
|
||
private void SynchronizeAllFilteredItems() | ||
{ | ||
FilteredExternalTools.Clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it would make sense to build a collection first and then assign it to FilteredExternalTools at the end. Otherwise you'll be generated a bunch of changed events (no items, then 1 item, then 2 items, etc)... I bet the titlebar could potentially flicker based on system load? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, that could only happen on first init, since the user is only adding/removing one tool at a time. If we reinstate the ability to bulk edit the json, then when we read it in, it would again be an init.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh... yeah, I misread that.
[JsonIgnore] | ||
|
||
[ObservableProperty] | ||
private bool _isPinned; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm struggling with this one a bit.... first we've got the pinned property, and then potientially an "order index in the bar"... is this a property of an ExternalTool, or how it's represented in the bar? Or is it ok to have them all in one data structure.
Not advocating for a particular change now, but just something to keep in mind in the future.
#Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IsPinned is rightfully a property of ExternalTool, same as arguments - both are PI-specific configurations for the tool that the user gets to choose. We use the IsPinned user setting to do X, and the Args user setting to do Y. Not sure where the "order index" comes from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We chatted a bit about long term vision. I'm ok with this for now.
} | ||
|
||
// The bar shows only the pinned tools. | ||
public ObservableCollection<ExternalTool> FilteredExternalTools { get; private set; } = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should probably be in the BarWindow view model vs here... since the BarWindow would want a filtered view based on pinned, but other components using this (thinking about when we need input/output filters, extensions, etc) would want a different set of filtered tools. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and that's a can of worms I want to defer to next round. ExternalToolsHelper should be split into a BarViewModel and other stuff.
|
||
// Note the additional "property:" syntax to ensure the JsonIgnore is propagated to the generated property. | ||
[ObservableProperty] | ||
[property: JsonIgnore] | ||
private SoftwareBitmapSource? _toolIcon; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that would be my preferred style, but I'm seeing the leading underscore in other parts of DevHome. We should make an explicit style choice across the board, and do a code cleanup operation at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary of the pull request
Prior to this, when the user added an external tool we always pinned it to the bar. Now, while we always add it to the menu, we let the user choose whether to pin it to the bar (defaults to pinned). This meant adding an IsPinned property to ExternalTool, a ToggleSwitch in the Settings | AddTool UI, adding a FilteredExternalTools collection which is synchronized with changes to the "all tools" collection, and databinding the buttons collection to this instead. The ExternalTools menu uses the "all tools" collection.
Fixed related bugs