code_review: PR #1532

- remove `MenuItemExtension`
- display `Tag` as hotkey tips when `InputGesture` is null
- remove `Avalonia.Input` reference in `ViewModels`
- rewrite `InteractiveRebase` view to avoid using `Tag` in menu item

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo
2025-07-07 12:44:31 +08:00
parent dd3c658a0d
commit b85f100d84
8 changed files with 86 additions and 124 deletions

View File

@@ -855,27 +855,24 @@
</ContentPresenter.DataTemplates>
</ContentPresenter>
<TextBlock x:Name="PART_InputGestureText"
Grid.Column="2"
<TextBlock Grid.Column="2"
Classes="CaptionTextBlockStyle"
Margin="{DynamicResource MenuInputGestureTextMargin}"
Text="{TemplateBinding InputGesture, Converter={StaticResource KeyGestureConverter}}"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"
FontSize="11"
IsVisible="{TemplateBinding (v:MenuItemExtension.Command), Converter={x:Static StringConverters.IsNullOrEmpty}}"/>
FontSize="11"/>
<TextBlock x:Name="PART_CommandTip"
Grid.Column="2"
<TextBlock Grid.Column="2"
Classes="CaptionTextBlockStyle"
Margin="{DynamicResource MenuInputGestureTextMargin}"
Text="{TemplateBinding (v:MenuItemExtension.Command)}"
Text="{TemplateBinding Tag}"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Foreground="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"
FontSize="11"
IsVisible="{TemplateBinding (v:MenuItemExtension.Command), Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
IsVisible="{TemplateBinding InputGesture, Converter={x:Static ObjectConverters.IsNull}}"/>
<Path Name="PART_ChevronPath"
Width="6"
@@ -931,9 +928,6 @@
<Style Selector="^ /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemForegroundPointerOver}" />
</Style>
<Style Selector="^ /template/ TextBlock#PART_InputGestureText">
<Setter Property="Foreground" Value="{DynamicResource MenuFlyoutItemKeyboardAcceleratorTextForegroundPointerOver}" />
</Style>
<Style Selector="^ /template/ Path#PART_ChevronPath">
<Setter Property="Fill" Value="{DynamicResource MenuFlyoutSubItemChevronPointerOver}" />
</Style>

View File

@@ -1,11 +1,10 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -429,7 +428,7 @@ namespace SourceGit.ViewModels
var copyMultipleInfo = new MenuItem();
copyMultipleInfo.Header = App.Text("CommitCM.CopySHA") + " - " + App.Text("CommitCM.CopySubject");
copyMultipleInfo.Icon = App.CreateMenuIcon("Icons.Info");
copyMultipleInfo.InputGesture = new KeyGesture(Key.C, OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control);
copyMultipleInfo.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
copyMultipleInfo.Click += async (_, e) =>
{
var builder = new StringBuilder();
@@ -726,7 +725,7 @@ namespace SourceGit.ViewModels
var createBranch = new MenuItem();
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
createBranch.Header = App.Text("CreateBranch");
createBranch.InputGesture = new KeyGesture(Key.B, OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control);
createBranch.Tag = OperatingSystem.IsMacOS() ? "⌘+B" : "Ctrl+B";
createBranch.Click += (_, e) =>
{
if (_repo.CanCreatePopup())
@@ -842,7 +841,7 @@ namespace SourceGit.ViewModels
var copyInfo = new MenuItem();
copyInfo.Header = App.Text("CommitCM.CopySHA") + " - " + App.Text("CommitCM.CopySubject");
copyInfo.Icon = App.CreateMenuIcon("Icons.Info");
copyInfo.InputGesture = new KeyGesture(Key.C, OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control);
copyInfo.Tag = OperatingSystem.IsMacOS() ? "⌘+C" : "Ctrl+C";
copyInfo.Click += async (_, e) =>
{
await App.CopyTextAsync($"{commit.SHA.AsSpan(0, 10)} - {commit.Subject}");

View File

@@ -3,7 +3,6 @@ using System.IO;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -468,7 +467,7 @@ namespace SourceGit.ViewModels
var menu = new ContextMenu();
var close = new MenuItem();
close.Header = App.Text("PageTabBar.Tab.Close");
close.InputGesture = KeyGesture.Parse(OperatingSystem.IsMacOS() ? "⌘+W" : "Ctrl+W");
close.Tag = OperatingSystem.IsMacOS() ? "⌘+W" : "Ctrl+W";
close.Click += (_, e) =>
{
CloseTab(page);

View File

@@ -1775,7 +1775,7 @@ namespace SourceGit.ViewModels
var dateOrder = new MenuItem();
dateOrder.Header = App.Text("Repository.HistoriesOrder.ByDate");
dateOrder.SetValue(Views.MenuItemExtension.CommandProperty, "--date-order");
dateOrder.Tag = "--date-order";
if (!_settings.EnableTopoOrderInHistories)
dateOrder.Icon = App.CreateMenuIcon("Icons.Check");
dateOrder.Click += (_, ev) =>
@@ -1791,7 +1791,7 @@ namespace SourceGit.ViewModels
var topoOrder = new MenuItem();
topoOrder.Header = App.Text("Repository.HistoriesOrder.Topo");
topoOrder.SetValue(Views.MenuItemExtension.CommandProperty, "--top-order");
topoOrder.Tag = "--topo-order";
if (_settings.EnableTopoOrderInHistories)
topoOrder.Icon = App.CreateMenuIcon("Icons.Check");
topoOrder.Click += (_, ev) =>

View File

@@ -4,7 +4,6 @@ using System.IO;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
@@ -677,7 +676,7 @@ namespace SourceGit.ViewModels
var stage = new MenuItem();
stage.Header = App.Text("FileCM.Stage");
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
stage.InputGesture = new KeyGesture(Key.Enter);
stage.Tag = "Enter/Space";
stage.Click += (_, e) =>
{
StageChanges(_selectedUnstaged, null);
@@ -687,7 +686,7 @@ namespace SourceGit.ViewModels
var discard = new MenuItem();
discard.Header = App.Text("FileCM.Discard");
discard.Icon = App.CreateMenuIcon("Icons.Undo");
discard.InputGesture = new KeyGesture(Key.Delete);
discard.Tag = "Back/Delete";
discard.Click += (_, e) =>
{
Discard(_selectedUnstaged);
@@ -1082,7 +1081,7 @@ namespace SourceGit.ViewModels
var stage = new MenuItem();
stage.Header = App.Text("FileCM.StageMulti", _selectedUnstaged.Count);
stage.Icon = App.CreateMenuIcon("Icons.File.Add");
stage.InputGesture = new KeyGesture(Key.Enter);
stage.Tag = "Enter/Space";
stage.Click += (_, e) =>
{
StageChanges(_selectedUnstaged, null);
@@ -1092,7 +1091,7 @@ namespace SourceGit.ViewModels
var discard = new MenuItem();
discard.Header = App.Text("FileCM.DiscardMulti", _selectedUnstaged.Count);
discard.Icon = App.CreateMenuIcon("Icons.Undo");
discard.InputGesture = new KeyGesture(Key.Delete);
discard.Tag = "Back/Delete";
discard.Click += (_, e) =>
{
Discard(_selectedUnstaged);
@@ -1268,7 +1267,7 @@ namespace SourceGit.ViewModels
var unstage = new MenuItem();
unstage.Header = App.Text("FileCM.Unstage");
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
unstage.InputGesture = new KeyGesture(Key.Enter);
unstage.Tag = "Enter/Space";
unstage.Click += (_, e) =>
{
UnstageChanges(_selectedStaged, null);
@@ -1472,7 +1471,7 @@ namespace SourceGit.ViewModels
var unstage = new MenuItem();
unstage.Header = App.Text("FileCM.UnstageMulti", _selectedStaged.Count);
unstage.Icon = App.CreateMenuIcon("Icons.File.Remove");
unstage.InputGesture = new KeyGesture(Key.Enter);
unstage.Tag = "Enter/Space";
unstage.Click += (_, e) =>
{
UnstageChanges(_selectedStaged, null);

View File

@@ -102,83 +102,7 @@
</Border>
<!-- Action -->
<Button Grid.Column="1" Opacity="1" Margin="4,0,0,0" Padding="8,2" Background="Transparent">
<Button.Flyout>
<MenuFlyout Placement="BottomEdgeAlignedLeft" VerticalOffset="-4">
<MenuItem InputGesture="P" Click="OnChangeRebaseAction" Tag="{x:Static m:InteractiveRebaseAction.Pick}">
<MenuItem.Icon>
<Ellipse Width="14" Height="14" Fill="Green"/>
</MenuItem.Icon>
<MenuItem.Header>
<Grid ColumnDefinitions="64,240">
<TextBlock Grid.Column="0" Classes="primary" Margin="4,0" Text="Pick"/>
<TextBlock Grid.Column="1" Text="Use this commit" Foreground="{DynamicResource Brush.FG2}"/>
</Grid>
</MenuItem.Header>
</MenuItem>
<MenuItem InputGesture="E" Click="OnChangeRebaseAction" Tag="{x:Static m:InteractiveRebaseAction.Edit}">
<MenuItem.Icon>
<Ellipse Width="14" Height="14" Fill="Orange"/>
</MenuItem.Icon>
<MenuItem.Header>
<Grid ColumnDefinitions="64,240">
<TextBlock Grid.Column="0" Classes="primary" Margin="4,0" Text="Edit"/>
<TextBlock Grid.Column="1" Text="Stop for amending" Foreground="{DynamicResource Brush.FG2}"/>
</Grid>
</MenuItem.Header>
</MenuItem>
<MenuItem InputGesture="R" Click="OnChangeRebaseAction" Tag="{x:Static m:InteractiveRebaseAction.Reword}">
<MenuItem.Icon>
<Ellipse Width="14" Height="14" Fill="Orange"/>
</MenuItem.Icon>
<MenuItem.Header>
<Grid ColumnDefinitions="64,240">
<TextBlock Grid.Column="0" Classes="primary" Margin="4,0" Text="Reword"/>
<TextBlock Grid.Column="1" Text="Edit the commit message" Foreground="{DynamicResource Brush.FG2}"/>
</Grid>
</MenuItem.Header>
</MenuItem>
<MenuItem InputGesture="S" Click="OnChangeRebaseAction" Tag="{x:Static m:InteractiveRebaseAction.Squash}" IsVisible="{Binding CanSquashOrFixup}">
<MenuItem.Icon>
<Ellipse Width="14" Height="14" Fill="LightGray"/>
</MenuItem.Icon>
<MenuItem.Header>
<Grid ColumnDefinitions="64,240">
<TextBlock Grid.Column="0" Classes="primary" Margin="4,0" Text="Squash"/>
<TextBlock Grid.Column="1" Text="Meld into previous commit" Foreground="{DynamicResource Brush.FG2}"/>
</Grid>
</MenuItem.Header>
</MenuItem>
<MenuItem InputGesture="F" Click="OnChangeRebaseAction" Tag="{x:Static m:InteractiveRebaseAction.Fixup}" IsVisible="{Binding CanSquashOrFixup}">
<MenuItem.Icon>
<Ellipse Width="14" Height="14" Fill="LightGray"/>
</MenuItem.Icon>
<MenuItem.Header>
<Grid ColumnDefinitions="64,240">
<TextBlock Grid.Column="0" Classes="primary" Margin="4,0" Text="Fixup"/>
<TextBlock Grid.Column="1" Text="Like 'Squash' but discard message" Foreground="{DynamicResource Brush.FG2}"/>
</Grid>
</MenuItem.Header>
</MenuItem>
<MenuItem InputGesture="D" Click="OnChangeRebaseAction" Tag="{x:Static m:InteractiveRebaseAction.Drop}">
<MenuItem.Icon>
<Ellipse Width="14" Height="14" Fill="Red"/>
</MenuItem.Icon>
<MenuItem.Header>
<Grid ColumnDefinitions="64,240">
<TextBlock Grid.Column="0" Classes="primary" Margin="4,0" Text="Drop"/>
<TextBlock Grid.Column="1" Text="Remove commit" Foreground="{DynamicResource Brush.FG2}"/>
</Grid>
</MenuItem.Header>
</MenuItem>
</MenuFlyout>
</Button.Flyout>
<Button Grid.Column="1" Opacity="1" Margin="4,0,0,0" Padding="8,2" Background="Transparent" Click="OnButtonActionClicked">
<StackPanel Orientation="Horizontal">
<Ellipse Width="14" Height="14" Fill="{Binding Action, Converter={x:Static c:InteractiveRebaseActionConverters.ToIconBrush}}"/>
<TextBlock Classes="primary" Margin="8,0" Text="{Binding Action, Converter={x:Static c:InteractiveRebaseActionConverters.ToName}}"/>

View File

@@ -1,8 +1,10 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
namespace SourceGit.Views
{
@@ -126,16 +128,31 @@ namespace SourceGit.Views
}
}
private void OnChangeRebaseAction(object sender, RoutedEventArgs e)
private void OnButtonActionClicked(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.InteractiveRebase vm &&
sender is Control
{
DataContext: ViewModels.InteractiveRebaseItem item,
Tag: Models.InteractiveRebaseAction action
})
vm.ChangeAction(item, action);
if (DataContext is not ViewModels.InteractiveRebase vm)
return;
if (sender is not Button { DataContext: ViewModels.InteractiveRebaseItem item } button)
return;
var flyout = new MenuFlyout();
flyout.Placement = PlacementMode.BottomEdgeAlignedLeft;
flyout.VerticalOffset = -4;
CreateActionMenuItem(flyout, Brushes.Green, "Pick", "Use this commit", "P", item, Models.InteractiveRebaseAction.Pick);
CreateActionMenuItem(flyout, Brushes.Orange, "Edit", "Stop for amending", "E", item, Models.InteractiveRebaseAction.Edit);
CreateActionMenuItem(flyout, Brushes.Orange, "Reword", "Edit the commit message", "R", item, Models.InteractiveRebaseAction.Reword);
if (item.CanSquashOrFixup)
{
CreateActionMenuItem(flyout, Brushes.LightGray, "Squash", "Meld into previous commit", "S", item, Models.InteractiveRebaseAction.Squash);
CreateActionMenuItem(flyout, Brushes.LightGray, "Fixup", "Like 'Squash' but discard message", "F", item, Models.InteractiveRebaseAction.Fixup);
}
CreateActionMenuItem(flyout, Brushes.Red, "Drop", "Remove commit", "D", item, Models.InteractiveRebaseAction.Drop);
flyout.ShowAt(button);
e.Handled = true;
}
@@ -163,5 +180,46 @@ namespace SourceGit.Views
Running.IsVisible = false;
Close();
}
private void CreateActionMenuItem(MenuFlyout flyout, IBrush iconBrush, string label, string desc, string hotkey, ViewModels.InteractiveRebaseItem item, Models.InteractiveRebaseAction action)
{
var header = new Grid()
{
ColumnDefinitions =
[
new ColumnDefinition(64, GridUnitType.Pixel),
new ColumnDefinition(240, GridUnitType.Pixel),
],
Children =
{
new TextBlock()
{
[Grid.ColumnProperty] = 0,
Margin = new Thickness(4, 0),
Text = label
},
new TextBlock()
{
[Grid.ColumnProperty] = 1,
Text = desc,
Foreground = this.FindResource("Brush.FG2") as SolidColorBrush,
}
}
};
var menuItem = new MenuItem();
menuItem.Icon = new Ellipse() { Width = 14, Height = 14, Fill = iconBrush };
menuItem.Header = header;
menuItem.Tag = hotkey;
menuItem.Click += (_, e) =>
{
if (DataContext is ViewModels.InteractiveRebase vm)
vm.ChangeAction(item, action);
e.Handled = true;
};
flyout.Items.Add(menuItem);
}
}
}

View File

@@ -1,11 +0,0 @@
using Avalonia;
using Avalonia.Controls;
namespace SourceGit.Views
{
public class MenuItemExtension : AvaloniaObject
{
public static readonly AttachedProperty<string> CommandProperty =
AvaloniaProperty.RegisterAttached<MenuItemExtension, MenuItem, string>("Command", string.Empty);
}
}