Merge claudedo/923d3b07183c4ca8bcd15aed12f9df39
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
|
||||
xmlns:islands="using:ClaudeDo.Ui.Views.Islands"
|
||||
xmlns:converters="using:Avalonia.Data.Converters"
|
||||
xmlns:uiconverters="using:ClaudeDo.Ui.Converters"
|
||||
xmlns:loc="using:ClaudeDo.Ui.Localization"
|
||||
x:Class="ClaudeDo.Ui.Views.Islands.TasksIslandView"
|
||||
x:DataType="vm:TasksIslandViewModel">
|
||||
@@ -89,82 +89,64 @@
|
||||
Command="{Binding OpenNotesCommand}"
|
||||
Content="{loc:Tr tasks.notesPinnedRow}"/>
|
||||
|
||||
<!-- Task list -->
|
||||
<ScrollViewer>
|
||||
<StackPanel Margin="10,4">
|
||||
|
||||
<!-- OVERDUE -->
|
||||
<StackPanel IsVisible="{Binding HasOverdue}">
|
||||
<TextBlock Classes="eyebrow section-label overdue"
|
||||
Text="{loc:Tr tasks.overdue}" Margin="14,14,14,6"/>
|
||||
<ItemsControl ItemsSource="{Binding OverdueItems}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:TaskRowViewModel">
|
||||
<Button Classes="flat" HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Command="{Binding $parent[ItemsControl].((vm:TasksIslandViewModel)DataContext).SelectCommand}"
|
||||
CommandParameter="{Binding}">
|
||||
<islands:TaskRowView/>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
<!-- TASKS -->
|
||||
<StackPanel IsVisible="{Binding HasOpen}">
|
||||
<TextBlock Classes="eyebrow section-label"
|
||||
Text="{loc:Tr tasks.tasks}" Margin="14,14,14,6"
|
||||
IsVisible="{Binding ShowOpenLabel}"/>
|
||||
<ItemsControl ItemsSource="{Binding OpenItems}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:TaskRowViewModel">
|
||||
<Button Classes="flat" HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Command="{Binding $parent[ItemsControl].((vm:TasksIslandViewModel)DataContext).SelectCommand}"
|
||||
CommandParameter="{Binding}">
|
||||
<islands:TaskRowView/>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
<!-- COMPLETED -->
|
||||
<StackPanel>
|
||||
<StackPanel.IsVisible>
|
||||
<MultiBinding Converter="{x:Static converters:BoolConverters.And}">
|
||||
<Binding Path="HasCompleted"/>
|
||||
<Binding Path="IsShowingCompleted"/>
|
||||
</MultiBinding>
|
||||
</StackPanel.IsVisible>
|
||||
<!-- Task list: one flat, virtualized ListBox. Rows is HeaderRow | TaskRowViewModel — group
|
||||
headers are regular entries, not a separate ItemsControl per section, so a
|
||||
VirtualizingStackPanel actually bounds the realized container count. -->
|
||||
<ListBox x:Name="RowsListBox"
|
||||
ItemsSource="{Binding Rows}"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Padding="10,4">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel/>
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.Styles>
|
||||
<!-- SelectedItem is never bound — selection is handled entirely by the inner row Button's
|
||||
SelectCommand, mirroring the pre-virtualization ItemsControl. Avalonia's ListBox has
|
||||
no "no selection" mode, so neutralize the app-wide selected/pointerover
|
||||
ContentPresenter overlay (App.axaml) instead of letting it double up with
|
||||
TaskRowView's own hover/selected style. -->
|
||||
<Style Selector="ListBoxItem">
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="MinHeight" Value="0"/>
|
||||
<Setter Property="CornerRadius" Value="0"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Focusable" Value="{Binding Converter={x:Static uiconverters:NotHeaderRowConverter.Instance}}"/>
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
<ListBox.DataTemplates>
|
||||
<DataTemplate DataType="vm:HeaderRow">
|
||||
<Grid ColumnDefinitions="*,Auto" Margin="14,14,14,6">
|
||||
<TextBlock Grid.Column="0" Classes="eyebrow section-label"
|
||||
Text="{Binding CompletedHeader}" VerticalAlignment="Center"/>
|
||||
<Button Grid.Column="1" Classes="icon-btn"
|
||||
Command="{Binding ClearCompletedCommand}"
|
||||
Classes.overdue="{Binding IsOverdue}"
|
||||
Text="{Binding Label}" VerticalAlignment="Center"/>
|
||||
<Button Grid.Column="1" Classes="icon-btn" IsVisible="{Binding HasAction}"
|
||||
Command="{Binding ActionCommand}"
|
||||
ToolTip.Tip="{loc:Tr tasks.clearCompletedTip}"
|
||||
VerticalAlignment="Center">
|
||||
<PathIcon Data="{StaticResource Icon.Trash}" Width="13" Height="13"
|
||||
Foreground="{DynamicResource BloodBrush}"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
<ItemsControl ItemsSource="{Binding CompletedItems}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="vm:TaskRowViewModel">
|
||||
<Button Classes="flat" HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Command="{Binding $parent[ItemsControl].((vm:TasksIslandViewModel)DataContext).SelectCommand}"
|
||||
CommandParameter="{Binding}">
|
||||
<islands:TaskRowView/>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="vm:TaskRowViewModel">
|
||||
<Button Classes="flat" HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Command="{Binding $parent[ListBox].((vm:TasksIslandViewModel)DataContext).SelectCommand}"
|
||||
CommandParameter="{Binding}">
|
||||
<islands:TaskRowView/>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ListBox.DataTemplates>
|
||||
</ListBox>
|
||||
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
@@ -62,19 +61,14 @@ public partial class TasksIslandView : UserControl
|
||||
};
|
||||
}
|
||||
|
||||
// Bring the selected row into view — the task list is a plain ItemsControl with no
|
||||
// built-in selection scrolling, so a programmatic select (e.g. Mission Control's
|
||||
// "Open in app") would otherwise highlight a row that stays off-screen.
|
||||
// Bring the selected row into view — a programmatic select (e.g. Mission Control's "Open in
|
||||
// app") can target a row that isn't currently realized. RowsListBox.ScrollIntoView is
|
||||
// virtualization-aware (it realizes the container as needed); a visual-tree search for an
|
||||
// existing Button, as this used to do, only finds rows already on screen.
|
||||
private void ScrollSelectedIntoView()
|
||||
{
|
||||
if (DataContext is not TasksIslandViewModel vm || vm.SelectedTask is not { } target) return;
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
var match = this.GetVisualDescendants()
|
||||
.OfType<Button>()
|
||||
.FirstOrDefault(b => ReferenceEquals(b.DataContext, target));
|
||||
match?.BringIntoView();
|
||||
}, DispatcherPriority.Background);
|
||||
Dispatcher.UIThread.Post(() => RowsListBox.ScrollIntoView(target), DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task<bool> ShowConfirmAsync(string message)
|
||||
@@ -363,13 +357,12 @@ public partial class TasksIslandView : UserControl
|
||||
return null;
|
||||
}
|
||||
|
||||
// The next row in Rows is only "in the same section" while it's a TaskRowViewModel —
|
||||
// hitting a HeaderRow (or the end of the list) means row was the last one in its section.
|
||||
private static TaskRowViewModel? FindNextInSameSection(TasksIslandViewModel vm, TaskRowViewModel row)
|
||||
{
|
||||
foreach (var section in new[] { vm.OverdueItems, vm.OpenItems, vm.CompletedItems })
|
||||
{
|
||||
var idx = section.IndexOf(row);
|
||||
if (idx >= 0) return idx + 1 < section.Count ? section[idx + 1] : null;
|
||||
}
|
||||
return null;
|
||||
var idx = vm.Rows.IndexOf(row);
|
||||
if (idx < 0 || idx + 1 >= vm.Rows.Count) return null;
|
||||
return vm.Rows[idx + 1] as TaskRowViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user