Files
ClaudeDo/src/ClaudeDo.Ui/Views/TaskListView.axaml.cs
mika kuns 32bb52875f feat(ui): add subtask tree view with expand/collapse in task list
Tasks with subtasks show a chevron for inline expand/collapse.
Subtask checkboxes toggle completion state directly. Also sets
Windows AppUserModelID for proper taskbar identity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:16:22 +02:00

129 lines
4.0 KiB
C#

using System.Collections.ObjectModel;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
using ClaudeDo.Ui.ViewModels;
namespace ClaudeDo.Ui.Views;
public partial class TaskListView : UserControl
{
public TaskListView()
{
InitializeComponent();
}
private void OnInlineAddKeyDown(object? sender, KeyEventArgs e)
{
if (DataContext is not TaskListViewModel vm) return;
if (e.Key == Key.Enter)
{
vm.InlineAddCommand.Execute(null);
e.Handled = true;
}
else if (e.Key == Key.Escape)
{
vm.InlineAddTitle = "";
this.FindControl<ListBox>("TaskListBox")?.Focus();
e.Handled = true;
}
}
private void OnInlineAddGotFocus(object? sender, FocusChangedEventArgs e)
{
if (sender is TextBox tb)
tb.BorderBrush = Avalonia.Application.Current?.FindResource("AccentBrush") as Avalonia.Media.IBrush;
}
private void OnInlineAddLostFocus(object? sender, RoutedEventArgs e)
{
if (sender is TextBox tb)
tb.BorderBrush = Avalonia.Application.Current?.FindResource("BorderSubtleBrush") as Avalonia.Media.IBrush;
}
private void OnTaskListKeyDown(object? sender, KeyEventArgs e)
{
if (DataContext is not TaskListViewModel vm || vm.SelectedTask is null) return;
switch (e.Key)
{
case Key.Delete:
vm.DeleteTaskCommand.Execute(null);
e.Handled = true;
break;
case Key.Space:
if (vm.SelectedTask.CanToggleDone)
{
vm.SelectedTask.ToggleDoneCommand.Execute(null);
e.Handled = true;
}
break;
case Key.Enter:
case Key.F2:
var detailView = this.GetVisualAncestors().OfType<Window>().FirstOrDefault()
?.GetVisualDescendants().OfType<TaskDetailView>().FirstOrDefault();
detailView?.FocusTitle();
e.Handled = true;
break;
}
}
private void OnCheckboxPressed(object? sender, PointerPressedEventArgs e)
{
if (sender is not Border { DataContext: TaskItemViewModel task }) return;
if (task.CanToggleDone)
{
task.ToggleDoneCommand.Execute(null);
e.Handled = true;
}
}
private void OnTaskItemDoubleTapped(object? sender, TappedEventArgs e)
{
if (DataContext is TaskListViewModel vm)
vm.EditTaskCommand.Execute(null);
}
private void OnTaskItemPointerPressed(object? sender, PointerPressedEventArgs e)
{
var props = e.GetCurrentPoint(this).Properties;
if (!props.IsRightButtonPressed) return;
if (sender is Grid { DataContext: TaskItemViewModel item }
&& DataContext is TaskListViewModel vm)
{
vm.SelectedTask = item;
}
}
private void OnSubtaskPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (e.GetCurrentPoint(null).Properties.IsRightButtonPressed
&& sender is Control { DataContext: SubtaskItemViewModel subtask }
&& DataContext is TaskListViewModel vm)
{
var parent = vm.Tasks.FirstOrDefault(t => t.Subtasks.Contains(subtask));
if (parent is not null)
vm.SelectedTask = parent;
}
}
private async void OnSubtaskCheckboxClick(object? sender, RoutedEventArgs e)
{
if (sender is CheckBox { DataContext: SubtaskItemViewModel subtask }
&& DataContext is TaskListViewModel vm)
{
var parent = vm.Tasks.FirstOrDefault(t => t.Subtasks.Contains(subtask));
if (parent is not null)
await parent.ToggleSubtaskDoneCommand.ExecuteAsync(subtask.Id);
}
}
public void FocusInlineAdd()
{
this.FindControl<TextBox>("InlineAddBox")?.Focus();
}
}