perf(ui): build task row context menu lazily on right-click
This commit is contained in:
@@ -31,58 +31,8 @@
|
|||||||
Margin="0"
|
Margin="0"
|
||||||
Classes.selected="{Binding IsSelected}"
|
Classes.selected="{Binding IsSelected}"
|
||||||
Classes.dragging="{Binding IsDragging}"
|
Classes.dragging="{Binding IsDragging}"
|
||||||
Classes.done="{Binding Done}">
|
Classes.done="{Binding Done}"
|
||||||
<Border.ContextMenu>
|
ContextRequested="OnRowContextRequested">
|
||||||
<ContextMenu>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxSendToQueue}"
|
|
||||||
IsVisible="{Binding CanSendToQueue}"
|
|
||||||
Click="OnSendToQueueClick"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxRemoveFromQueue}"
|
|
||||||
IsVisible="{Binding CanRemoveFromQueue}"
|
|
||||||
Click="OnRemoveFromQueueClick"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxCancelExecution}"
|
|
||||||
IsVisible="{Binding IsRunning}"
|
|
||||||
Click="OnCancelExecutionClick"/>
|
|
||||||
<Separator/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxMarkAs}">
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxMarkDone}" Tag="Done" Click="OnSetStatusClick"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxMarkCancelled}" Tag="Cancelled" Click="OnSetStatusClick"/>
|
|
||||||
<Separator/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxMarkManual}"
|
|
||||||
IsVisible="{Binding !IsManual}"
|
|
||||||
Click="OnToggleManualClick"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxMarkClaudeTask}"
|
|
||||||
IsVisible="{Binding IsManual}"
|
|
||||||
Click="OnToggleManualClick"/>
|
|
||||||
</MenuItem>
|
|
||||||
<Separator/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxOpenConPtySession}"
|
|
||||||
Click="OnOpenConPtySessionClick"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxOpenPlanningSession}"
|
|
||||||
Click="OnOpenPlanningSessionClick"
|
|
||||||
IsVisible="{Binding CanOpenPlanningSession}"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxResumePlanningSession}"
|
|
||||||
Click="OnResumePlanningSessionClick"
|
|
||||||
IsVisible="{Binding CanResumeOrDiscardPlanning}"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxFinalizePlanningSession}"
|
|
||||||
Click="OnFinalizePlanningSessionClick"
|
|
||||||
IsVisible="{Binding CanFinalizePlanning}"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxDiscardPlanningSession}"
|
|
||||||
Click="OnDiscardPlanningSessionClick"
|
|
||||||
IsVisible="{Binding CanResumeOrDiscardPlanning}"/>
|
|
||||||
<Separator/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxScheduleFor}" Click="OnScheduleForClick"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxClearSchedule}"
|
|
||||||
IsVisible="{Binding HasSchedule}"
|
|
||||||
Click="OnClearScheduleClick"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxAddToMyDay}"
|
|
||||||
IsVisible="{Binding CanAddToMyDay}"
|
|
||||||
Click="OnAddToMyDayClick"/>
|
|
||||||
<MenuItem Header="{loc:Tr tasks.ctxRemoveFromMyDay}"
|
|
||||||
IsVisible="{Binding IsMyDay}"
|
|
||||||
Click="OnRemoveFromMyDayClick"/>
|
|
||||||
</ContextMenu>
|
|
||||||
</Border.ContextMenu>
|
|
||||||
<Grid ColumnDefinitions="0,18,32,*,Auto,Auto,32" Margin="6,8,10,8">
|
<Grid ColumnDefinitions="0,18,32,*,Auto,Auto,32" Margin="6,8,10,8">
|
||||||
|
|
||||||
<!-- Chevron toggle (only for planning parent tasks) -->
|
<!-- Chevron toggle (only for planning parent tasks) -->
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
using ClaudeDo.Data.Models;
|
using ClaudeDo.Data.Models;
|
||||||
|
using ClaudeDo.Ui.Localization;
|
||||||
using ClaudeDo.Ui.ViewModels.Islands;
|
using ClaudeDo.Ui.ViewModels.Islands;
|
||||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||||
|
|
||||||
@@ -19,6 +21,57 @@ public partial class TaskRowView : UserControl
|
|||||||
this.GetVisualAncestors().OfType<ItemsControl>()
|
this.GetVisualAncestors().OfType<ItemsControl>()
|
||||||
.Select(ic => ic.DataContext).OfType<TasksIslandViewModel>().FirstOrDefault();
|
.Select(ic => ic.DataContext).OfType<TasksIslandViewModel>().FirstOrDefault();
|
||||||
|
|
||||||
|
// The context menu is built here instead of declared in XAML so the ~18 MenuItems only
|
||||||
|
// exist while a row's menu is actually open, not for every row rendered in the list.
|
||||||
|
private void OnRowContextRequested(object? sender, ContextRequestedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is not Border border) return;
|
||||||
|
if (DataContext is not TaskRowViewModel row) return;
|
||||||
|
if (FindTasksVm() is not { } vm) return;
|
||||||
|
|
||||||
|
// OnTunnelPointerPressed (TasksIslandView) only selects on the left button, so
|
||||||
|
// right-click needs its own explicit selection before the menu opens.
|
||||||
|
vm.SelectedTask = row;
|
||||||
|
|
||||||
|
var menu = new ContextMenu { DataContext = row };
|
||||||
|
|
||||||
|
MenuItem MakeItem(string key, EventHandler<RoutedEventArgs> click, bool isVisible = true, string? tag = null)
|
||||||
|
{
|
||||||
|
var item = new MenuItem { Header = Loc.T(key), DataContext = row, IsVisible = isVisible };
|
||||||
|
if (tag is not null) item.Tag = tag;
|
||||||
|
item.Click += click;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxSendToQueue", OnSendToQueueClick, row.CanSendToQueue));
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxRemoveFromQueue", OnRemoveFromQueueClick, row.CanRemoveFromQueue));
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxCancelExecution", OnCancelExecutionClick, row.IsRunning));
|
||||||
|
menu.Items.Add(new Separator());
|
||||||
|
|
||||||
|
var markAs = new MenuItem { Header = Loc.T("tasks.ctxMarkAs"), DataContext = row };
|
||||||
|
markAs.Items.Add(MakeItem("tasks.ctxMarkDone", OnSetStatusClick, tag: "Done"));
|
||||||
|
markAs.Items.Add(MakeItem("tasks.ctxMarkCancelled", OnSetStatusClick, tag: "Cancelled"));
|
||||||
|
markAs.Items.Add(new Separator());
|
||||||
|
markAs.Items.Add(MakeItem("tasks.ctxMarkManual", OnToggleManualClick, !row.IsManual));
|
||||||
|
markAs.Items.Add(MakeItem("tasks.ctxMarkClaudeTask", OnToggleManualClick, row.IsManual));
|
||||||
|
menu.Items.Add(markAs);
|
||||||
|
|
||||||
|
menu.Items.Add(new Separator());
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxOpenConPtySession", OnOpenConPtySessionClick));
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxOpenPlanningSession", OnOpenPlanningSessionClick, row.CanOpenPlanningSession));
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxResumePlanningSession", OnResumePlanningSessionClick, row.CanResumeOrDiscardPlanning));
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxFinalizePlanningSession", OnFinalizePlanningSessionClick, row.CanFinalizePlanning));
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxDiscardPlanningSession", OnDiscardPlanningSessionClick, row.CanResumeOrDiscardPlanning));
|
||||||
|
menu.Items.Add(new Separator());
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxScheduleFor", OnScheduleForClick));
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxClearSchedule", OnClearScheduleClick, row.HasSchedule));
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxAddToMyDay", OnAddToMyDayClick, row.CanAddToMyDay));
|
||||||
|
menu.Items.Add(MakeItem("tasks.ctxRemoveFromMyDay", OnRemoveFromMyDayClick, row.IsMyDay));
|
||||||
|
|
||||||
|
menu.Open(border);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private async void OnSendToQueueClick(object? sender, RoutedEventArgs e)
|
private async void OnSendToQueueClick(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
|
if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)
|
||||||
|
|||||||
Reference in New Issue
Block a user