New list view items.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
using Windows.System;
|
using Microsoft.UI.Input;
|
||||||
|
using Windows.System;
|
||||||
using Windows.UI.Core;
|
using Windows.UI.Core;
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
|
||||||
namespace Wino.Core.WinUI.Services;
|
namespace Wino.Core.WinUI.Services;
|
||||||
@@ -8,8 +8,8 @@ namespace Wino.Core.WinUI.Services;
|
|||||||
public class KeyPressService : IKeyPressService
|
public class KeyPressService : IKeyPressService
|
||||||
{
|
{
|
||||||
public bool IsCtrlKeyPressed()
|
public bool IsCtrlKeyPressed()
|
||||||
=> Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down) ?? false;
|
=> InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down);
|
||||||
|
|
||||||
public bool IsShiftKeyPressed()
|
public bool IsShiftKeyPressed()
|
||||||
=> Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down) ?? false;
|
=> InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -724,6 +724,27 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
|||||||
await NotifySelectionChangesAsync();
|
await NotifySelectionChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IMailListItem> AllItemsIncludingThreads
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
foreach (var group in _mailItemSource)
|
||||||
|
{
|
||||||
|
foreach (var item in group)
|
||||||
|
{
|
||||||
|
if (item is ThreadMailItemViewModel threadMailItemViewModel)
|
||||||
|
{
|
||||||
|
foreach (var child in threadMailItemViewModel.ThreadEmails)
|
||||||
|
{
|
||||||
|
yield return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerable<MailItemViewModel> AllItems
|
private IEnumerable<MailItemViewModel> AllItems
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -752,7 +773,7 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
|||||||
public bool IsAllItemsSelected => AllItems.Any() && AllItems.All(a => a.IsSelected);
|
public bool IsAllItemsSelected => AllItems.Any() && AllItems.All(a => a.IsSelected);
|
||||||
public bool HasSingleItemSelected => SelectedItemsCount == 1;
|
public bool HasSingleItemSelected => SelectedItemsCount == 1;
|
||||||
|
|
||||||
public async Task ExecuteWithoutRaiseSelectionChangedAsync(Action<MailItemViewModel> action)
|
public async Task ExecuteWithoutRaiseSelectionChangedAsync(Action<IMailListItem> action, bool includeThreads)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -760,11 +781,21 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
|||||||
Messenger.Unregister<SelectedItemsChangedMessage>(this);
|
Messenger.Unregister<SelectedItemsChangedMessage>(this);
|
||||||
|
|
||||||
await ExecuteUIThread(() =>
|
await ExecuteUIThread(() =>
|
||||||
|
{
|
||||||
|
if (includeThreads)
|
||||||
|
{
|
||||||
|
foreach (var item in AllItemsIncludingThreads)
|
||||||
|
{
|
||||||
|
action(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
foreach (var item in AllItems)
|
foreach (var item in AllItems)
|
||||||
{
|
{
|
||||||
action(item);
|
action(item);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
@@ -816,8 +847,9 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SelectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = true);
|
public Task SelectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = true, true);
|
||||||
public Task UnselectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = false);
|
public Task UnselectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = false, true);
|
||||||
|
public Task CollapseAllThreadsAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => { if (a is ThreadMailItemViewModel thread) thread.IsThreadExpanded = false; }, true);
|
||||||
|
|
||||||
private async Task ExecuteUIThread(Action action) => await CoreDispatcher?.ExecuteOnUIThread(action);
|
private async Task ExecuteUIThread(Action action) => await CoreDispatcher?.ExecuteOnUIThread(action);
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Wino.Mail.ViewModels.Data;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Single view model for IMailItem representation.
|
/// Single view model for IMailItem representation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class MailItemViewModel(MailCopy mailCopy) : ObservableObject, IMailListItem
|
public partial class MailItemViewModel(MailCopy mailCopy) : ObservableRecipient, IMailListItem
|
||||||
{
|
{
|
||||||
public DateTime CreationDate => MailCopy.CreationDate;
|
public DateTime CreationDate => MailCopy.CreationDate;
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
@@ -18,6 +18,7 @@ public partial class MailItemViewModel(MailCopy mailCopy) : ObservableObject, IM
|
|||||||
public partial bool ThumbnailUpdatedEvent { get; set; } = false;
|
public partial bool ThumbnailUpdatedEvent { get; set; } = false;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
|
[NotifyPropertyChangedRecipients]
|
||||||
public partial bool IsSelected { get; set; }
|
public partial bool IsSelected { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
@@ -89,10 +90,6 @@ public partial class MailItemViewModel(MailCopy mailCopy) : ObservableObject, IM
|
|||||||
set => SetProperty(MailCopy.HasAttachments, value, MailCopy, (u, n) => u.HasAttachments = n);
|
set => SetProperty(MailCopy.HasAttachments, value, MailCopy, (u, n) => u.HasAttachments = n);
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnIsSelectedChanged(bool oldValue, bool newValue)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Guid> GetContainingIds() => [MailCopy.UniqueId];
|
public IEnumerable<Guid> GetContainingIds() => [MailCopy.UniqueId];
|
||||||
|
|
||||||
public IEnumerable<MailItemViewModel> GetSelectedMailItems()
|
public IEnumerable<MailItemViewModel> GetSelectedMailItems()
|
||||||
|
|||||||
@@ -17,11 +17,16 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IDisposable,
|
|||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
[NotifyPropertyChangedRecipients]
|
[NotifyPropertyChangedRecipients]
|
||||||
|
[NotifyPropertyChangedFor(nameof(IsSelectedOrExpanded))]
|
||||||
public partial bool IsThreadExpanded { get; set; }
|
public partial bool IsThreadExpanded { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
|
[NotifyPropertyChangedRecipients]
|
||||||
|
[NotifyPropertyChangedFor(nameof(IsSelectedOrExpanded))]
|
||||||
public partial bool IsSelected { get; set; }
|
public partial bool IsSelected { get; set; }
|
||||||
|
|
||||||
|
public bool IsSelectedOrExpanded => IsSelected || IsThreadExpanded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the number of emails in this thread
|
/// Gets the number of emails in this thread
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
using MoreLinq;
|
using MoreLinq;
|
||||||
using Nito.AsyncEx;
|
using Nito.AsyncEx;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@@ -39,7 +40,8 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
IRecipient<NewMailSynchronizationRequested>,
|
IRecipient<NewMailSynchronizationRequested>,
|
||||||
IRecipient<AccountSynchronizerStateChanged>,
|
IRecipient<AccountSynchronizerStateChanged>,
|
||||||
IRecipient<AccountCacheResetMessage>,
|
IRecipient<AccountCacheResetMessage>,
|
||||||
IRecipient<ThumbnailAdded>
|
IRecipient<ThumbnailAdded>,
|
||||||
|
IRecipient<PropertyChangedMessage<bool>>
|
||||||
{
|
{
|
||||||
private bool isChangingFolder = false;
|
private bool isChangingFolder = false;
|
||||||
|
|
||||||
@@ -221,7 +223,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
var selectedItem = MailCollection.SelectedItems.ElementAtOrDefault(0);
|
var selectedItem = MailCollection.SelectedItems.ElementAtOrDefault(0);
|
||||||
ActiveMailItemChanged(selectedItem);
|
ActiveMailItemChanged(selectedItem);
|
||||||
}
|
}
|
||||||
else if (MailCollection.SelectedItemsCount > 1)
|
else if (MailCollection.SelectedItemsCount == 0)
|
||||||
{
|
{
|
||||||
ActiveMailItemChanged(null);
|
ActiveMailItemChanged(null);
|
||||||
}
|
}
|
||||||
@@ -311,20 +313,6 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
{
|
{
|
||||||
if (_activeMailItem == selectedMailItemViewModel) return;
|
if (_activeMailItem == selectedMailItemViewModel) return;
|
||||||
|
|
||||||
// Don't update active mail item if Ctrl key is pressed or multi selection is enabled.
|
|
||||||
// User is probably trying to select multiple items.
|
|
||||||
// This is not the same behavior in Windows Mail,
|
|
||||||
// but it's a trash behavior.
|
|
||||||
|
|
||||||
var isCtrlKeyPressed = _keyPressService.IsCtrlKeyPressed();
|
|
||||||
|
|
||||||
bool isMultiSelecting = isCtrlKeyPressed || IsMultiSelectionModeEnabled;
|
|
||||||
|
|
||||||
if (isMultiSelecting && StatePersistenceService.IsReaderNarrowed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_activeMailItem = selectedMailItemViewModel;
|
_activeMailItem = selectedMailItemViewModel;
|
||||||
|
|
||||||
Messenger.Send(new ActiveMailItemChangedEvent(_activeMailItem));
|
Messenger.Send(new ActiveMailItemChangedEvent(_activeMailItem));
|
||||||
@@ -1102,6 +1090,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
Messenger.Register<AccountSynchronizerStateChanged>(this);
|
Messenger.Register<AccountSynchronizerStateChanged>(this);
|
||||||
Messenger.Register<AccountCacheResetMessage>(this);
|
Messenger.Register<AccountCacheResetMessage>(this);
|
||||||
Messenger.Register<ThumbnailAdded>(this);
|
Messenger.Register<ThumbnailAdded>(this);
|
||||||
|
Messenger.Register<PropertyChangedMessage<bool>>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UnregisterRecipients()
|
protected override void UnregisterRecipients()
|
||||||
@@ -1115,5 +1104,26 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
|||||||
Messenger.Unregister<AccountSynchronizerStateChanged>(this);
|
Messenger.Unregister<AccountSynchronizerStateChanged>(this);
|
||||||
Messenger.Unregister<AccountCacheResetMessage>(this);
|
Messenger.Unregister<AccountCacheResetMessage>(this);
|
||||||
Messenger.Unregister<ThumbnailAdded>(this);
|
Messenger.Unregister<ThumbnailAdded>(this);
|
||||||
|
Messenger.Unregister<PropertyChangedMessage<bool>>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Receive(PropertyChangedMessage<bool> message)
|
||||||
|
{
|
||||||
|
// Handle IsSelected property changes from MailItemViewModel
|
||||||
|
if (message.PropertyName == nameof(MailItemViewModel.IsSelected) && message.Sender is MailItemViewModel mailItemViewModel)
|
||||||
|
{
|
||||||
|
Messenger.Send(new SelectedItemsChangedMessage());
|
||||||
|
}
|
||||||
|
else if (message.Sender is ThreadMailItemViewModel threadMailItemViewModel)
|
||||||
|
{
|
||||||
|
if (message.PropertyName == nameof(ThreadMailItemViewModel.IsSelected))
|
||||||
|
{
|
||||||
|
// Thread selected.
|
||||||
|
}
|
||||||
|
else if (message.PropertyName == nameof(ThreadMailItemViewModel.IsThreadExpanded))
|
||||||
|
{
|
||||||
|
// Thread expanded.
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.UI.Xaml;
|
using System.Linq;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Wino.Mail.ViewModels.Data;
|
using Wino.Mail.ViewModels.Data;
|
||||||
|
|
||||||
@@ -6,10 +7,20 @@ namespace Wino.Mail.WinUI.Controls.ListView;
|
|||||||
|
|
||||||
public partial class WinoListView : Microsoft.UI.Xaml.Controls.ListView
|
public partial class WinoListView : Microsoft.UI.Xaml.Controls.ListView
|
||||||
{
|
{
|
||||||
|
public bool IsThreadListView
|
||||||
|
{
|
||||||
|
get { return (bool)GetValue(IsThreadListViewProperty); }
|
||||||
|
set { SetValue(IsThreadListViewProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty IsThreadListViewProperty = DependencyProperty.Register(nameof(IsThreadListView), typeof(bool), typeof(WinoListView), new PropertyMetadata(false));
|
||||||
|
|
||||||
public bool IsAllSelected => Items.Count == SelectedItems.Count;
|
public bool IsAllSelected => Items.Count == SelectedItems.Count;
|
||||||
|
|
||||||
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
|
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
|
||||||
{
|
{
|
||||||
|
base.PrepareContainerForItemOverride(element, item);
|
||||||
|
|
||||||
if (item is MailItemViewModel mailItemViewModel && element is WinoMailItemViewModelListViewItem container)
|
if (item is MailItemViewModel mailItemViewModel && element is WinoMailItemViewModelListViewItem container)
|
||||||
{
|
{
|
||||||
// Ensure the container's selection state matches the model's state
|
// Ensure the container's selection state matches the model's state
|
||||||
@@ -19,18 +30,52 @@ public partial class WinoListView : Microsoft.UI.Xaml.Controls.ListView
|
|||||||
}
|
}
|
||||||
else if (item is ThreadMailItemViewModel threadMailItemViewModel && element is WinoThreadMailItemViewModelListViewItem threadContainer)
|
else if (item is ThreadMailItemViewModel threadMailItemViewModel && element is WinoThreadMailItemViewModelListViewItem threadContainer)
|
||||||
{
|
{
|
||||||
|
threadContainer.IsSelected = threadMailItemViewModel.IsSelected;
|
||||||
threadContainer.IsThreadExpanded = threadMailItemViewModel.IsThreadExpanded;
|
threadContainer.IsThreadExpanded = threadMailItemViewModel.IsThreadExpanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.PrepareContainerForItemOverride(element, item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
|
public WinoMailItemViewModelListViewItem? GetMailItemContainer(MailItemViewModel mailItemViewModel)
|
||||||
{
|
{
|
||||||
if (element is WinoThreadMailItemViewModelListViewItem threadMailItemViewModelListViewItem) threadMailItemViewModelListViewItem.Cleanup();
|
foreach (var item in Items)
|
||||||
if (element is WinoMailItemViewModelListViewItem winoMailItemViewModelListViewItem) winoMailItemViewModelListViewItem.Cleanup();
|
{
|
||||||
|
if (item is MailItemViewModel mailItem && mailItem.Id == mailItemViewModel.Id) return ContainerFromItem(mailItemViewModel) as WinoMailItemViewModelListViewItem;
|
||||||
|
if (item is ThreadMailItemViewModel threadMailItem && threadMailItem.GetContainingIds().Contains(mailItemViewModel.MailCopy.UniqueId))
|
||||||
|
{
|
||||||
|
var threadContainer = ContainerFromItem(threadMailItem) as WinoThreadMailItemViewModelListViewItem;
|
||||||
|
|
||||||
base.ClearContainerForItemOverride(element, item);
|
// Try to get the inner WinoListView.
|
||||||
|
if (threadContainer != null)
|
||||||
|
{
|
||||||
|
var innerListViewControl = threadContainer.GetWinoListViewControl();
|
||||||
|
|
||||||
|
return innerListViewControl?.ContainerFromItem(mailItemViewModel) as WinoMailItemViewModelListViewItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WinoThreadMailItemViewModelListViewItem? GetThreadMailItemContainer(ThreadMailItemViewModel threadMailItemViewModel)
|
||||||
|
=> ContainerFromItem(threadMailItemViewModel) as WinoThreadMailItemViewModelListViewItem;
|
||||||
|
|
||||||
|
public void ToggleItemContainer(IMailListItem mailListItem)
|
||||||
|
{
|
||||||
|
DispatcherQueue.TryEnqueue(() =>
|
||||||
|
{
|
||||||
|
if (mailListItem is MailItemViewModel mailItemViewModel)
|
||||||
|
{
|
||||||
|
var container = GetMailItemContainer(mailItemViewModel);
|
||||||
|
container?.IsSelected = mailItemViewModel.IsSelected;
|
||||||
|
}
|
||||||
|
else if (mailListItem is ThreadMailItemViewModel threadMailItemViewModel)
|
||||||
|
{
|
||||||
|
var container = GetThreadMailItemContainer(threadMailItemViewModel);
|
||||||
|
container?.IsSelected = threadMailItemViewModel.IsSelected;
|
||||||
|
container?.IsThreadExpanded = threadMailItemViewModel.IsThreadExpanded;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SelectMailItemContainer(MailItemViewModel mailItemViewModel)
|
public bool SelectMailItemContainer(MailItemViewModel mailItemViewModel)
|
||||||
|
|||||||
@@ -11,37 +11,76 @@
|
|||||||
|
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<!-- Thread Mail ListViewItem Style -->
|
<!-- Thread Mail ListViewItem Style -->
|
||||||
<Style x:Key="DefaultThreadListViewItemStyle" TargetType="local:WinoMailItemViewModelListViewItem">
|
<Style
|
||||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
x:Key="DefaultThreadListViewItemStyle"
|
||||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
BasedOn="{StaticResource DefaultListViewItemStyle}"
|
||||||
<Setter Property="Background" Value="{ThemeResource ListViewItemBackground}" />
|
TargetType="local:WinoThreadMailItemViewModelListViewItem">
|
||||||
<Setter Property="Foreground" Value="{ThemeResource ListViewItemForeground}" />
|
|
||||||
<Setter Property="TabNavigation" Value="Local" />
|
|
||||||
<Setter Property="IsHoldingEnabled" Value="True" />
|
|
||||||
<Setter Property="Padding" Value="16,0,12,0" />
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
|
||||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
|
||||||
<Setter Property="MinWidth" Value="{ThemeResource ListViewItemMinWidth}" />
|
|
||||||
<Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}" />
|
|
||||||
<Setter Property="AllowDrop" Value="False" />
|
|
||||||
<Setter Property="UseSystemFocusVisuals" Value="True" />
|
|
||||||
<Setter Property="FocusVisualMargin" Value="1" />
|
|
||||||
<Setter Property="FocusVisualPrimaryBrush" Value="{ThemeResource ListViewItemFocusVisualPrimaryBrush}" />
|
|
||||||
<Setter Property="FocusVisualPrimaryThickness" Value="2" />
|
|
||||||
<Setter Property="FocusVisualSecondaryBrush" Value="{ThemeResource ListViewItemFocusVisualSecondaryBrush}" />
|
|
||||||
<Setter Property="FocusVisualSecondaryThickness" Value="1" />
|
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="local:WinoMailItemViewModelListViewItem">
|
<ControlTemplate TargetType="local:WinoThreadMailItemViewModelListViewItem">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="8" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
x:Name="RootGrid"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Background="{ThemeResource ListViewItemBackground}"
|
||||||
|
CornerRadius="{ThemeResource ControlCornerRadius}" />
|
||||||
|
<Border
|
||||||
|
x:Name="SelectionIndicator"
|
||||||
|
Width="4"
|
||||||
|
Margin="0,16"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Background="{ThemeResource ListViewItemSelectionIndicatorBrush}"
|
||||||
|
CornerRadius="4"
|
||||||
|
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Item.IsSelected, Mode=OneWay}" />
|
||||||
<!-- Expandable Content -->
|
<!-- Expandable Content -->
|
||||||
<ContentPresenter
|
<ContentPresenter
|
||||||
x:Name="ThreadContent"
|
x:Name="ThreadContent"
|
||||||
Margin="{TemplateBinding Padding}"
|
Grid.Column="1"
|
||||||
|
Margin="6,0,0,0"
|
||||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
Content="{TemplateBinding Content}"
|
Content="{TemplateBinding Content}"
|
||||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||||
|
<VisualStateManager.VisualStateGroups>
|
||||||
|
<VisualStateGroup x:Name="CommonStates">
|
||||||
|
<VisualState x:Name="Normal" />
|
||||||
|
<VisualState x:Name="PointerOver">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundPointerOver}" />
|
||||||
|
<Setter Target="ThreadContent.Foreground" Value="{ThemeResource ListViewItemForegroundPointerOver}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="Selected">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelected}" />
|
||||||
|
<Setter Target="ThreadContent.Foreground" Value="{ThemeResource ListViewItemForegroundSelected}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="SelectedPointerOver">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelectedPointerOver}" />
|
||||||
|
<Setter Target="ThreadContent.Foreground" Value="{ThemeResource ListViewItemForegroundSelected}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="Pressed">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundPressed}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="PressedSelected">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelectedPressed}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
</VisualStateManager.VisualStateGroups>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
@@ -52,7 +91,76 @@
|
|||||||
<Style
|
<Style
|
||||||
x:Key="DefaultMailListViewItemStyle"
|
x:Key="DefaultMailListViewItemStyle"
|
||||||
BasedOn="{StaticResource DefaultListViewItemStyle}"
|
BasedOn="{StaticResource DefaultListViewItemStyle}"
|
||||||
TargetType="local:WinoMailItemViewModelListViewItem" />
|
TargetType="local:WinoMailItemViewModelListViewItem">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="local:WinoMailItemViewModelListViewItem">
|
||||||
|
<Grid
|
||||||
|
x:Name="RootGrid"
|
||||||
|
Margin="0,2"
|
||||||
|
Background="{ThemeResource ListViewItemBackground}"
|
||||||
|
CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="8" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Border
|
||||||
|
x:Name="SelectionIndicator"
|
||||||
|
Width="4"
|
||||||
|
Margin="0,16"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Background="{ThemeResource ListViewItemSelectionIndicatorBrush}"
|
||||||
|
CornerRadius="4"
|
||||||
|
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Item.IsSelected, Mode=OneWay}" />
|
||||||
|
|
||||||
|
<ContentPresenter
|
||||||
|
x:Name="MailContent"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="6,0,0,0"
|
||||||
|
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
Background="Transparent"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||||
|
<VisualStateManager.VisualStateGroups>
|
||||||
|
<VisualStateGroup x:Name="CommonStates">
|
||||||
|
<VisualState x:Name="Normal" />
|
||||||
|
<VisualState x:Name="PointerOver">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundPointerOver}" />
|
||||||
|
<Setter Target="MailContent.Foreground" Value="{ThemeResource ListViewItemForegroundPointerOver}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="Selected">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelected}" />
|
||||||
|
<Setter Target="MailContent.Foreground" Value="{ThemeResource ListViewItemForegroundSelected}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="SelectedPointerOver">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelectedPointerOver}" />
|
||||||
|
<Setter Target="MailContent.Foreground" Value="{ThemeResource ListViewItemForegroundSelected}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="Pressed">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundPressed}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
<VisualState x:Name="PressedSelected">
|
||||||
|
<VisualState.Setters>
|
||||||
|
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelectedPressed}" />
|
||||||
|
</VisualState.Setters>
|
||||||
|
</VisualState>
|
||||||
|
</VisualStateGroup>
|
||||||
|
</VisualStateManager.VisualStateGroups>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
<local:WinoMailItemContainerStyleSelector
|
<local:WinoMailItemContainerStyleSelector
|
||||||
x:Name="WinoMailItemContainerStyleSelector"
|
x:Name="WinoMailItemContainerStyleSelector"
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ public partial class WinoMailItemContainerStyleSelector : StyleSelector
|
|||||||
protected override Style SelectStyleCore(object item, DependencyObject container)
|
protected override Style SelectStyleCore(object item, DependencyObject container)
|
||||||
{
|
{
|
||||||
if (item is MailItemViewModel) return MailItemStyle ?? throw new Exception($"Missing style for {nameof(MailItemViewModel)}");
|
if (item is MailItemViewModel) return MailItemStyle ?? throw new Exception($"Missing style for {nameof(MailItemViewModel)}");
|
||||||
if (item is ThreadMailItemViewModel) return ThreadStyle ?? throw new Exception($"Missing style for {nameof(ThreadMailItemViewModel)}");
|
if (item is ThreadMailItemViewModel)
|
||||||
|
return ThreadStyle ?? throw new Exception($"Missing style for {nameof(ThreadMailItemViewModel)}");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,86 +1,23 @@
|
|||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.WinUI;
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Wino.Mail.ViewModels.Data;
|
using Wino.Mail.ViewModels.Data;
|
||||||
using Wino.Messaging.Client.Mails;
|
|
||||||
|
|
||||||
namespace Wino.Mail.WinUI.Controls.ListView;
|
namespace Wino.Mail.WinUI.Controls.ListView;
|
||||||
|
|
||||||
public partial class WinoMailItemViewModelListViewItem : ListViewItem
|
public partial class WinoMailItemViewModelListViewItem : ListViewItem
|
||||||
{
|
{
|
||||||
private readonly long _selectionChangeCallbackToken;
|
[GeneratedDependencyProperty]
|
||||||
|
public partial MailItemViewModel? Item { get; set; }
|
||||||
|
|
||||||
public WinoMailItemViewModelListViewItem()
|
public WinoMailItemViewModelListViewItem()
|
||||||
{
|
{
|
||||||
DefaultStyleKey = typeof(WinoMailItemViewModelListViewItem);
|
DefaultStyleKey = typeof(WinoMailItemViewModelListViewItem);
|
||||||
|
|
||||||
_selectionChangeCallbackToken = RegisterPropertyChangedCallback(IsSelectedProperty, OnIsSelectedChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnContentChanged(object oldContent, object newContent)
|
protected override void OnContentChanged(object oldContent, object newContent)
|
||||||
{
|
{
|
||||||
base.OnContentChanged(oldContent, newContent);
|
base.OnContentChanged(oldContent, newContent);
|
||||||
|
|
||||||
if (oldContent is MailItemViewModel oldMailItem)
|
if (newContent is MailItemViewModel mailItemViewModel) Item = mailItemViewModel;
|
||||||
{
|
|
||||||
UnregisterSelectionCallback(oldMailItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newContent is MailItemViewModel newMailItem)
|
|
||||||
{
|
|
||||||
IsSelected = newMailItem.IsSelected;
|
|
||||||
RegisterSelectionCallback(newMailItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cleanup()
|
|
||||||
{
|
|
||||||
if (Content is MailItemViewModel mailItem)
|
|
||||||
{
|
|
||||||
UnregisterSelectionCallback(mailItem);
|
|
||||||
|
|
||||||
UnregisterPropertyChangedCallback(IsSelectedProperty, _selectionChangeCallbackToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UnregisterSelectionCallback(IMailListItem mailItem)
|
|
||||||
{
|
|
||||||
mailItem.PropertyChanged -= MailPropChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterSelectionCallback(IMailListItem mailItem)
|
|
||||||
{
|
|
||||||
mailItem.PropertyChanged -= MailPropChanged;
|
|
||||||
mailItem.PropertyChanged += MailPropChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
// From model
|
|
||||||
private void MailPropChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is not MailItemViewModel mailItem) return;
|
|
||||||
|
|
||||||
if (e.PropertyName == nameof(MailItemViewModel.IsSelected)) ApplySelectionForContainer(mailItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
// From container.
|
|
||||||
private void OnIsSelectedChanged(DependencyObject sender, DependencyProperty dp)
|
|
||||||
{
|
|
||||||
if (Content is IMailListItem mailItem)
|
|
||||||
{
|
|
||||||
ApplySelectionForModel(mailItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplySelectionForModel(IMailListItem mailItem)
|
|
||||||
{
|
|
||||||
mailItem.IsSelected = IsSelected;
|
|
||||||
WeakReferenceMessenger.Default.Send(new SelectedItemsChangedMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplySelectionForContainer(IMailListItem mailItem)
|
|
||||||
{
|
|
||||||
if (IsSelected != mailItem.IsSelected)
|
|
||||||
{
|
|
||||||
IsSelected = mailItem.IsSelected;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.Linq;
|
||||||
using System.Linq;
|
using CommunityToolkit.WinUI;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Wino.Controls;
|
using Wino.Controls;
|
||||||
@@ -10,30 +10,29 @@ namespace Wino.Mail.WinUI.Controls.ListView;
|
|||||||
|
|
||||||
public partial class WinoThreadMailItemViewModelListViewItem : ListViewItem
|
public partial class WinoThreadMailItemViewModelListViewItem : ListViewItem
|
||||||
{
|
{
|
||||||
public bool IsThreadExpanded
|
[GeneratedDependencyProperty]
|
||||||
|
public partial bool IsThreadExpanded { get; set; }
|
||||||
|
|
||||||
|
[GeneratedDependencyProperty]
|
||||||
|
public partial ThreadMailItemViewModel? Item { get; set; }
|
||||||
|
|
||||||
|
protected override void OnContentChanged(object oldContent, object newContent)
|
||||||
{
|
{
|
||||||
get { return (bool)GetValue(IsThreadExpandedProperty); }
|
base.OnContentChanged(oldContent, newContent);
|
||||||
set { SetValue(IsThreadExpandedProperty, value); }
|
|
||||||
|
if (newContent is ThreadMailItemViewModel threadMailItemViewModel) Item = threadMailItemViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty IsThreadExpandedProperty = DependencyProperty.Register(nameof(IsThreadExpanded), typeof(bool), typeof(WinoThreadMailItemViewModelListViewItem), new PropertyMetadata(false, new PropertyChangedCallback(OnIsThreadExpandedChanged)));
|
|
||||||
|
|
||||||
private readonly long _selectionChangeCallbackToken;
|
|
||||||
public WinoThreadMailItemViewModelListViewItem()
|
public WinoThreadMailItemViewModelListViewItem()
|
||||||
{
|
{
|
||||||
DefaultStyleKey = typeof(WinoThreadMailItemViewModelListViewItem);
|
DefaultStyleKey = typeof(WinoThreadMailItemViewModelListViewItem);
|
||||||
|
|
||||||
_selectionChangeCallbackToken = RegisterPropertyChangedCallback(IsSelectedProperty, OnIsSelectedChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cleanup()
|
partial void OnIsThreadExpandedChanged(bool newValue)
|
||||||
{
|
{
|
||||||
if (Content is ThreadMailItemViewModel mailItem)
|
// 1. Reflect expansion changes to WinoExpander.
|
||||||
{
|
// 2. Automatically select first item on expansion, if none selected.
|
||||||
UnregisterSelectionCallback(mailItem);
|
// 3. Unselect all items on collapse.
|
||||||
|
|
||||||
UnregisterPropertyChangedCallback(IsSelectedProperty, _selectionChangeCallbackToken);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnIsThreadExpandedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs dp)
|
private static void OnIsThreadExpandedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs dp)
|
||||||
@@ -42,79 +41,32 @@ public partial class WinoThreadMailItemViewModelListViewItem : ListViewItem
|
|||||||
// 2. Automatically select first item on expansion, if none selected.
|
// 2. Automatically select first item on expansion, if none selected.
|
||||||
// 3. Unselect all items on collapse.
|
// 3. Unselect all items on collapse.
|
||||||
|
|
||||||
var control = sender as WinoThreadMailItemViewModelListViewItem;
|
//var control = sender as WinoThreadMailItemViewModelListViewItem;
|
||||||
|
|
||||||
var innerControl = control?.GetWinoListViewControl();
|
//var innerControl = control?.GetWinoListViewControl();
|
||||||
var expander = control?.GetExpander();
|
//var expander = control?.GetExpander();
|
||||||
|
|
||||||
if (innerControl == null || control == null || expander == null) return;
|
//if (innerControl == null || control == null || expander == null) return;
|
||||||
|
|
||||||
|
|
||||||
// 2
|
//// 2
|
||||||
if (control.IsThreadExpanded && innerControl.SelectedItems.Count == 0 && innerControl.Items.Count > 0)
|
//if (control.IsThreadExpanded && innerControl.SelectedItems.Count == 0 && innerControl.Items.Count > 0)
|
||||||
{
|
//{
|
||||||
innerControl.SelectedItems.Clear();
|
// innerControl.SelectedItems.Clear();
|
||||||
|
|
||||||
// Make item selected, container might not be realized yet, so set on the model.
|
// // Make item selected, container might not be realized yet, so set on the model.
|
||||||
// It'll appear selected when container is realized.
|
// // It'll appear selected when container is realized.
|
||||||
|
|
||||||
var firstItem = innerControl.Items.FirstOrDefault() as MailItemViewModel;
|
// var firstItem = innerControl.Items.FirstOrDefault() as MailItemViewModel;
|
||||||
|
|
||||||
firstItem?.IsSelected = true;
|
// firstItem?.IsSelected = true;
|
||||||
}
|
//}
|
||||||
|
|
||||||
// 1
|
//// 1
|
||||||
expander.IsExpanded = control.IsThreadExpanded;
|
//expander.IsExpanded = control.IsThreadExpanded;
|
||||||
|
|
||||||
// 3
|
//// 3
|
||||||
if (!control.IsSelected) innerControl?.SelectedItems.Clear();
|
//if (!control.IsSelected) innerControl?.SelectedItems.Clear();
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnContentChanged(object oldContent, object newContent)
|
|
||||||
{
|
|
||||||
base.OnContentChanged(oldContent, newContent);
|
|
||||||
|
|
||||||
if (oldContent is ThreadMailItemViewModel oldMailItem)
|
|
||||||
{
|
|
||||||
UnregisterSelectionCallback(oldMailItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newContent is ThreadMailItemViewModel newMailItem)
|
|
||||||
{
|
|
||||||
IsSelected = newMailItem.IsSelected;
|
|
||||||
RegisterSelectionCallback(newMailItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnIsSelectedChanged(DependencyObject sender, DependencyProperty dp)
|
|
||||||
{
|
|
||||||
IsThreadExpanded = IsSelected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnregisterSelectionCallback(ThreadMailItemViewModel mailItem)
|
|
||||||
{
|
|
||||||
mailItem.PropertyChanged -= MailPropChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MailPropChanged(object? sender, PropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is not ThreadMailItemViewModel mailItem) return;
|
|
||||||
|
|
||||||
if (e.PropertyName == nameof(ThreadMailItemViewModel.IsThreadExpanded))
|
|
||||||
{
|
|
||||||
ApplySelectionForContainer(mailItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterSelectionCallback(ThreadMailItemViewModel mailItem)
|
|
||||||
{
|
|
||||||
mailItem.PropertyChanged -= MailPropChanged;
|
|
||||||
mailItem.PropertyChanged += MailPropChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplySelectionForContainer(ThreadMailItemViewModel mailItem)
|
|
||||||
{
|
|
||||||
if (IsThreadExpanded != mailItem.IsThreadExpanded) IsThreadExpanded = mailItem.IsThreadExpanded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public WinoListView? GetWinoListViewControl()
|
public WinoListView? GetWinoListViewControl()
|
||||||
|
|||||||
@@ -69,7 +69,10 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="ThreadMailItemTemplate" x:DataType="viewModelData:ThreadMailItemViewModel">
|
<DataTemplate x:Key="ThreadMailItemTemplate" x:DataType="viewModelData:ThreadMailItemViewModel">
|
||||||
<controls:WinoExpander x:Name="ExpanderPart" IsExpanded="{x:Bind IsThreadExpanded, Mode=TwoWay}">
|
<controls:WinoExpander
|
||||||
|
x:Name="ExpanderPart"
|
||||||
|
Margin="0,2"
|
||||||
|
IsExpanded="{x:Bind IsThreadExpanded, Mode=TwoWay}">
|
||||||
<controls:WinoExpander.Header>
|
<controls:WinoExpander.Header>
|
||||||
<controls:MailItemDisplayInformationControl
|
<controls:MailItemDisplayInformationControl
|
||||||
x:DefaultBindMode="OneWay"
|
x:DefaultBindMode="OneWay"
|
||||||
@@ -95,11 +98,20 @@
|
|||||||
toolkitExt:ListViewExtensions.ItemContainerStretchDirection="Horizontal"
|
toolkitExt:ListViewExtensions.ItemContainerStretchDirection="Horizontal"
|
||||||
toolkitExt:ScrollViewerExtensions.VerticalScrollBarMargin="0"
|
toolkitExt:ScrollViewerExtensions.VerticalScrollBarMargin="0"
|
||||||
ChoosingItemContainer="WinoListViewChoosingItemContainer"
|
ChoosingItemContainer="WinoListViewChoosingItemContainer"
|
||||||
|
IsItemClickEnabled="True"
|
||||||
|
IsThreadListView="True"
|
||||||
|
ItemClick="WinoListViewItemClicked"
|
||||||
ItemContainerStyle="{StaticResource DefaultMailListViewItemStyle}"
|
ItemContainerStyle="{StaticResource DefaultMailListViewItemStyle}"
|
||||||
ItemTemplate="{StaticResource SingleMailItemTemplate}"
|
ItemTemplate="{StaticResource SingleMailItemTemplate}"
|
||||||
ItemsSource="{x:Bind ThreadEmails, Mode=OneTime}"
|
ItemsSource="{x:Bind ThreadEmails, Mode=OneTime}"
|
||||||
ProcessKeyboardAccelerators="WinoListViewProcessKeyboardAccelerators"
|
ProcessKeyboardAccelerators="WinoListViewProcessKeyboardAccelerators"
|
||||||
SelectionMode="Extended" />
|
SelectionMode="Extended">
|
||||||
|
<listview:WinoListView.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<ItemsStackPanel Margin="8,0,12,0" AreStickyGroupHeadersEnabled="True" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</listview:WinoListView.ItemsPanel>
|
||||||
|
</listview:WinoListView>
|
||||||
</controls:WinoExpander.Content>
|
</controls:WinoExpander.Content>
|
||||||
</controls:WinoExpander>
|
</controls:WinoExpander>
|
||||||
|
|
||||||
@@ -117,7 +129,7 @@
|
|||||||
|
|
||||||
<DataTemplate x:Key="MailGroupHeaderDefaultTemplate" x:DataType="collections:IReadOnlyObservableGroup">
|
<DataTemplate x:Key="MailGroupHeaderDefaultTemplate" x:DataType="collections:IReadOnlyObservableGroup">
|
||||||
<Grid
|
<Grid
|
||||||
Margin="4,2"
|
Margin="0,0,0,6"
|
||||||
AllowFocusOnInteraction="False"
|
AllowFocusOnInteraction="False"
|
||||||
Background="{ThemeResource MailListHeaderBackgroundColor}"
|
Background="{ThemeResource MailListHeaderBackgroundColor}"
|
||||||
CornerRadius="6">
|
CornerRadius="6">
|
||||||
@@ -386,12 +398,11 @@
|
|||||||
toolkitExt:ScrollViewerExtensions.VerticalScrollBarMargin="0"
|
toolkitExt:ScrollViewerExtensions.VerticalScrollBarMargin="0"
|
||||||
ChoosingItemContainer="WinoListViewChoosingItemContainer"
|
ChoosingItemContainer="WinoListViewChoosingItemContainer"
|
||||||
IsItemClickEnabled="True"
|
IsItemClickEnabled="True"
|
||||||
ItemClick="WinoListView_ItemClick"
|
ItemClick="WinoListViewItemClicked"
|
||||||
ItemContainerStyleSelector="{StaticResource WinoMailItemContainerStyleSelector}"
|
ItemContainerStyleSelector="{StaticResource WinoMailItemContainerStyleSelector}"
|
||||||
ItemTemplateSelector="{StaticResource MailItemTemplateSelector}"
|
ItemTemplateSelector="{StaticResource MailItemTemplateSelector}"
|
||||||
ItemsSource="{x:Bind MailCollectionViewSource.View, Mode=OneWay}"
|
ItemsSource="{x:Bind MailCollectionViewSource.View, Mode=OneWay}"
|
||||||
ProcessKeyboardAccelerators="WinoListViewProcessKeyboardAccelerators"
|
ProcessKeyboardAccelerators="WinoListViewProcessKeyboardAccelerators"
|
||||||
SelectionChanged="WinoListViewSelectionChanged"
|
|
||||||
SelectionMode="Extended">
|
SelectionMode="Extended">
|
||||||
<listview:WinoListView.ItemContainerTransitions>
|
<listview:WinoListView.ItemContainerTransitions>
|
||||||
<TransitionCollection>
|
<TransitionCollection>
|
||||||
@@ -400,7 +411,7 @@
|
|||||||
</listview:WinoListView.ItemContainerTransitions>
|
</listview:WinoListView.ItemContainerTransitions>
|
||||||
<listview:WinoListView.ItemsPanel>
|
<listview:WinoListView.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<ItemsStackPanel Margin="8,0,12,0" AreStickyGroupHeadersEnabled="True" />
|
<ItemsStackPanel AreStickyGroupHeadersEnabled="True" GroupPadding="10,10,10,0" />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</listview:WinoListView.ItemsPanel>
|
</listview:WinoListView.ItemsPanel>
|
||||||
<listview:WinoListView.Resources>
|
<listview:WinoListView.Resources>
|
||||||
@@ -412,15 +423,6 @@
|
|||||||
<GroupStyle HeaderTemplate="{StaticResource MailGroupHeaderDefaultTemplate}" HidesIfEmpty="True" />
|
<GroupStyle HeaderTemplate="{StaticResource MailGroupHeaderDefaultTemplate}" HidesIfEmpty="True" />
|
||||||
</listview:WinoListView.GroupStyle>
|
</listview:WinoListView.GroupStyle>
|
||||||
</listview:WinoListView>
|
</listview:WinoListView>
|
||||||
<!--<advanced:WinoItemsView
|
|
||||||
x:Name="MailListView"
|
|
||||||
Margin="4"
|
|
||||||
ItemTemplate="{StaticResource MailItemContainerSelector}"
|
|
||||||
ItemsSource="{x:Bind ViewModel.MailCollection.Items, Mode=OneTime}"
|
|
||||||
Layout="{StaticResource DefaultItemsViewLayout}"
|
|
||||||
LoadMoreCommand="{x:Bind ViewModel.LoadMoreItemsCommand}"
|
|
||||||
ProcessKeyboardAccelerators="MailListView_ProcessKeyboardAccelerators"
|
|
||||||
SelectionMode="Extended" />-->
|
|
||||||
|
|
||||||
<!-- Try online search panel. -->
|
<!-- Try online search panel. -->
|
||||||
<Grid Grid.Row="1" Visibility="{x:Bind ViewModel.IsOnlineSearchButtonVisible, Mode=OneWay}">
|
<Grid Grid.Row="1" Visibility="{x:Bind ViewModel.IsOnlineSearchButtonVisible, Mode=OneWay}">
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnNavigatedFrom(e);
|
base.OnNavigatedFrom(e);
|
||||||
@@ -162,6 +163,8 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
args.IsContainerPrepared = false;
|
args.IsContainerPrepared = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private async void MailItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
|
private async void MailItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
|
||||||
{
|
{
|
||||||
// TODO: New ItemsView implementation.
|
// TODO: New ItemsView implementation.
|
||||||
@@ -496,7 +499,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
|
|
||||||
private void UpdateAdaptiveness()
|
private void UpdateAdaptiveness()
|
||||||
{
|
{
|
||||||
bool isMultiSelectionEnabled = ViewModel.IsMultiSelectionModeEnabled || KeyPressService.IsCtrlKeyPressed();
|
bool isMultiSelectionEnabled = ViewModel.IsMultiSelectionModeEnabled;
|
||||||
|
|
||||||
if (StatePersistenceService.IsReaderNarrowed)
|
if (StatePersistenceService.IsReaderNarrowed)
|
||||||
{
|
{
|
||||||
@@ -545,22 +548,85 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WinoListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private async void WinoListViewItemClicked(object sender, ItemClickEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (sender is not WinoListView listView) return;
|
||||||
|
|
||||||
|
bool isSelectedItemFromThread = listView.IsThreadListView;
|
||||||
|
bool isCtrlPressed = KeyPressService.IsCtrlKeyPressed();
|
||||||
|
|
||||||
|
bool isClickingThreadItem = e.ClickedItem is ThreadMailItemViewModel;
|
||||||
|
|
||||||
|
// Unselect all items. It's single selection.
|
||||||
|
if (!isCtrlPressed)
|
||||||
|
{
|
||||||
|
await ViewModel.MailCollection.UnselectAllAsync();
|
||||||
|
|
||||||
|
if (!isSelectedItemFromThread && !isClickingThreadItem)
|
||||||
|
{
|
||||||
|
await ViewModel.MailCollection.CollapseAllThreadsAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WinoListView_ItemClick(object sender, ItemClickEventArgs e)
|
if (e.ClickedItem is MailItemViewModel mailListItem)
|
||||||
{
|
{
|
||||||
if (e.ClickedItem is ThreadMailItemViewModel clickedThread)
|
mailListItem.IsSelected = !mailListItem.IsSelected;
|
||||||
|
}
|
||||||
|
else if (e.ClickedItem is ThreadMailItemViewModel threadMailItemViewModel)
|
||||||
{
|
{
|
||||||
// Only if container is selected.
|
// Extended selection mode handling for threads
|
||||||
|
if (isCtrlPressed)
|
||||||
|
{
|
||||||
|
// If thread is selected and Ctrl is pressed
|
||||||
|
if (threadMailItemViewModel.IsSelected)
|
||||||
|
{
|
||||||
|
// If thread was collapsed, expand it
|
||||||
|
if (!threadMailItemViewModel.IsThreadExpanded)
|
||||||
|
{
|
||||||
|
threadMailItemViewModel.IsThreadExpanded = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check if all items are selected.
|
||||||
|
// If so, then unselect all items in the thread and unselect the thread itself.
|
||||||
|
if (threadMailItemViewModel.ThreadEmails.All(a => a.IsSelected))
|
||||||
|
{
|
||||||
|
foreach (var threadEmail in threadMailItemViewModel.ThreadEmails)
|
||||||
|
{
|
||||||
|
threadEmail.IsSelected = false;
|
||||||
|
}
|
||||||
|
threadMailItemViewModel.IsSelected = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If thread was already expanded, select all items in the thread
|
||||||
|
foreach (var threadEmail in threadMailItemViewModel.ThreadEmails)
|
||||||
|
{
|
||||||
|
threadEmail.IsSelected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Thread is not selected, select and expand it.
|
||||||
|
if (!threadMailItemViewModel.IsThreadExpanded) threadMailItemViewModel.IsThreadExpanded = true;
|
||||||
|
if (!threadMailItemViewModel.IsSelected)
|
||||||
|
{
|
||||||
|
threadMailItemViewModel.IsSelected = true;
|
||||||
|
|
||||||
var threadContainer = MailListView.ContainerFromItem(clickedThread) as WinoThreadMailItemViewModelListViewItem;
|
foreach (var threadEmail in threadMailItemViewModel.ThreadEmails)
|
||||||
|
|
||||||
if (threadContainer?.IsSelected ?? false)
|
|
||||||
{
|
{
|
||||||
clickedThread.IsThreadExpanded = !clickedThread.IsThreadExpanded;
|
threadEmail.IsSelected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No Ctrl pressed, toggle expansion state (default behavior)
|
||||||
|
threadMailItemViewModel.IsThreadExpanded = !threadMailItemViewModel.IsThreadExpanded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user