Some selections.
This commit is contained in:
@@ -1008,7 +1008,7 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task SelectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = true, true);
|
public Task SelectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = true, true);
|
||||||
public Task UnselectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = false, true);
|
public Task UnselectAllAsync(IMailListItem exceptItem = null) => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = false && a != exceptItem, true);
|
||||||
public Task CollapseAllThreadsAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => { if (a is ThreadMailItemViewModel thread) thread.IsThreadExpanded = false; }, true);
|
public Task CollapseAllThreadsAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => { if (a is ThreadMailItemViewModel thread) thread.IsThreadExpanded = false; }, true);
|
||||||
|
|
||||||
private Task ExecuteUIThread(Action action) => CoreDispatcher?.ExecuteOnUIThread(action);
|
private Task ExecuteUIThread(Action action) => CoreDispatcher?.ExecuteOnUIThread(action);
|
||||||
|
|||||||
@@ -558,90 +558,148 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
|||||||
{
|
{
|
||||||
if (clickedItem == null) return;
|
if (clickedItem == null) return;
|
||||||
|
|
||||||
bool isSelectedItemFromThread = listView.IsThreadListView;
|
// Requirements (summary):
|
||||||
|
// CTRL pressed -> multi-select behaviour
|
||||||
|
// * Clicking single item toggles only that item.
|
||||||
|
// * Clicking thread header toggles selection of thread AND all its children (all on or all off).
|
||||||
|
// * Clicking an item inside a thread toggles only that child item.
|
||||||
|
// CTRL NOT pressed -> single-select (exclusive) with toggle support (can leave zero selected)
|
||||||
|
// * Clicking thread header: unselect everything else, collapse all other threads, select only the thread + first child.
|
||||||
|
// If already in that state (thread selected and first child selected), clicking again unselects all (nothing selected).
|
||||||
|
// * Clicking a single (non-thread) item OR a child item: collapse & unselect all others then toggle that item's selection.
|
||||||
|
// If it was selected, result is nothing selected.
|
||||||
|
|
||||||
bool isCtrlPressed = KeyPressService.IsCtrlKeyPressed();
|
bool isCtrlPressed = KeyPressService.IsCtrlKeyPressed();
|
||||||
|
|
||||||
bool isClickingThreadItem = clickedItem is ThreadMailItemViewModel;
|
// Helper local to collapse all other threads (we always collapse ALL then possibly re-expand the active thread per rules)
|
||||||
|
async Task CollapseAllThreadsExceptAsync(ThreadMailItemViewModel? except)
|
||||||
// Unselect all items. It's single selection.
|
|
||||||
if (!isCtrlPressed)
|
|
||||||
{
|
{
|
||||||
await ViewModel.MailCollection.UnselectAllAsync();
|
await ViewModel.MailCollection.CollapseAllThreadsAsync();
|
||||||
|
if (except != null)
|
||||||
if (!isSelectedItemFromThread && !isClickingThreadItem)
|
|
||||||
{
|
{
|
||||||
await ViewModel.MailCollection.CollapseAllThreadsAsync();
|
// We'll expand explicitly when required by logic below.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clickedItem is MailItemViewModel mailListItem)
|
if (isCtrlPressed)
|
||||||
{
|
{
|
||||||
mailListItem.IsSelected = !mailListItem.IsSelected;
|
switch (clickedItem)
|
||||||
}
|
|
||||||
else if (clickedItem is ThreadMailItemViewModel threadMailItemViewModel)
|
|
||||||
{
|
|
||||||
// Extended selection mode handling for threads
|
|
||||||
if (isCtrlPressed)
|
|
||||||
{
|
{
|
||||||
// If thread is selected and Ctrl is pressed
|
case ThreadMailItemViewModel thread:
|
||||||
if (threadMailItemViewModel.IsSelected)
|
|
||||||
{
|
|
||||||
// If thread was collapsed, expand it
|
|
||||||
if (!threadMailItemViewModel.IsThreadExpanded)
|
|
||||||
{
|
{
|
||||||
threadMailItemViewModel.IsThreadExpanded = true;
|
// Determine if thread + all children currently selected
|
||||||
}
|
bool allSelected = thread.IsSelected && thread.ThreadEmails.All(e => e.IsSelected);
|
||||||
else
|
if (allSelected)
|
||||||
{
|
|
||||||
// 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)
|
// Unselect thread & all children
|
||||||
{
|
thread.IsSelected = false;
|
||||||
threadEmail.IsSelected = false;
|
foreach (var child in thread.ThreadEmails)
|
||||||
}
|
child.IsSelected = false;
|
||||||
threadMailItemViewModel.IsSelected = false;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If thread was already expanded, select all items in the thread
|
// Select thread & all children (do NOT disturb other selections in CTRL mode)
|
||||||
foreach (var threadEmail in threadMailItemViewModel.ThreadEmails)
|
thread.IsSelected = true;
|
||||||
{
|
foreach (var child in thread.ThreadEmails)
|
||||||
threadEmail.IsSelected = true;
|
child.IsSelected = true;
|
||||||
}
|
// Keep it expanded so user can see items
|
||||||
|
thread.IsThreadExpanded = true;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
case MailItemViewModel mail:
|
||||||
else
|
|
||||||
{
|
|
||||||
// Thread is not selected, select and expand it.
|
|
||||||
if (!threadMailItemViewModel.IsThreadExpanded) threadMailItemViewModel.IsThreadExpanded = true;
|
|
||||||
if (!threadMailItemViewModel.IsSelected)
|
|
||||||
{
|
{
|
||||||
threadMailItemViewModel.IsSelected = true;
|
// Toggle just this item; no collapse/unselect of others in multi-select mode.
|
||||||
|
mail.IsSelected = !mail.IsSelected;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; // Multi-select path ends here.
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var threadEmail in threadMailItemViewModel.ThreadEmails)
|
// SINGLE-SELECTION (exclusive) MODE WITH TOGGLE SUPPORT
|
||||||
{
|
if (clickedItem is ThreadMailItemViewModel clickedThread)
|
||||||
threadEmail.IsSelected = true;
|
{
|
||||||
}
|
bool wasThreadSelected = clickedThread.IsSelected;
|
||||||
|
|
||||||
|
// Reset everything first (exclusive selection scenario)
|
||||||
|
await ViewModel.MailCollection.UnselectAllAsync();
|
||||||
|
await CollapseAllThreadsExceptAsync(clickedThread);
|
||||||
|
|
||||||
|
if (wasThreadSelected)
|
||||||
|
{
|
||||||
|
// Toggle off -> leave nothing selected (all unselected, thread collapsed)
|
||||||
|
clickedThread.IsThreadExpanded = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select thread + first child only
|
||||||
|
clickedThread.IsSelected = true;
|
||||||
|
var firstChild = clickedThread.ThreadEmails.FirstOrDefault();
|
||||||
|
if (firstChild != null)
|
||||||
|
{
|
||||||
|
// Ensure only first child selected
|
||||||
|
foreach (var child in clickedThread.ThreadEmails)
|
||||||
|
{
|
||||||
|
child.IsSelected = child == firstChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clickedThread.IsThreadExpanded = true; // Show contents of active thread
|
||||||
|
}
|
||||||
|
else if (clickedItem is MailItemViewModel clickedMail)
|
||||||
|
{
|
||||||
|
bool wasSelected = clickedMail.IsSelected;
|
||||||
|
|
||||||
|
// Determine if this mail belongs to an already selected & expanded thread.
|
||||||
|
// If so, we only want to switch the selection inside that thread without collapsing or unselecting the thread header.
|
||||||
|
ThreadMailItemViewModel? parentThread = null;
|
||||||
|
|
||||||
|
foreach (var group in ViewModel.MailCollection.MailItems)
|
||||||
|
{
|
||||||
|
foreach (var item in group)
|
||||||
|
{
|
||||||
|
if (item is ThreadMailItemViewModel thread && thread.ThreadEmails.Contains(clickedMail))
|
||||||
|
{
|
||||||
|
parentThread = thread;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (parentThread != null) break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// No Ctrl pressed, toggle expansion state (default behavior)
|
|
||||||
threadMailItemViewModel.IsThreadExpanded = !threadMailItemViewModel.IsThreadExpanded;
|
|
||||||
|
|
||||||
// Select the first item in the thread if none is selected
|
bool isInSelectedExpandedThread = parentThread != null && parentThread.IsSelected && parentThread.IsThreadExpanded;
|
||||||
if (!threadMailItemViewModel.IsSelected)
|
|
||||||
|
if (isInSelectedExpandedThread)
|
||||||
|
{
|
||||||
|
// Switch selection within the thread: unselect previously selected children, select the clicked one.
|
||||||
|
if (parentThread?.ThreadEmails != null)
|
||||||
{
|
{
|
||||||
threadMailItemViewModel.IsSelected = true;
|
foreach (var child in parentThread.ThreadEmails)
|
||||||
var firstEmail = threadMailItemViewModel.ThreadEmails.FirstOrDefault();
|
{
|
||||||
firstEmail?.IsSelected = true;
|
child.IsSelected = child == clickedMail && !wasSelected; // If clicking an already selected child -> toggle off (none selected in thread except header)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wasSelected && parentThread != null)
|
||||||
|
{
|
||||||
|
// Clicking the already selected child should leave the thread header selected (canonical state: thread + first child previously).
|
||||||
|
// Decide whether to keep a child selected; spec wants toggle off allowed, so leave no child selected.
|
||||||
|
// Ensure parent thread stays selected & expanded.
|
||||||
|
parentThread.IsSelected = true;
|
||||||
|
parentThread.IsThreadExpanded = true;
|
||||||
|
}
|
||||||
|
return; // Done.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normal single-item (non-thread or entering a thread via child) behavior.
|
||||||
|
await ViewModel.MailCollection.UnselectAllAsync();
|
||||||
|
await ViewModel.MailCollection.CollapseAllThreadsAsync();
|
||||||
|
|
||||||
|
if (!wasSelected)
|
||||||
|
{
|
||||||
|
clickedMail.IsSelected = true; // Toggle on
|
||||||
|
}
|
||||||
|
// else leave all unselected (toggle off)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user