Skip to content

Commit 31000ec

Browse files
committed
Improve algorithm
1 parent 62dc257 commit 31000ec

7 files changed

Lines changed: 560 additions & 228 deletions

File tree

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using Muxarr.Data.Entities;
2+
3+
namespace Muxarr.Data.Extensions;
4+
5+
// A priority list falls into groups: an anchor (any entry that is not a fallback)
6+
// and the fallbacks directly under it. The editor reorders through these so a
7+
// group never gets split or silently re-parented.
8+
public static class LanguagePreferenceExtensions
9+
{
10+
/// <summary>
11+
/// The languages an entry is a fallback for: the ones above it in its group,
12+
/// in priority order. Empty when the entry is kept unconditionally.
13+
/// </summary>
14+
public static List<LanguagePreference> FallbackFor(this List<LanguagePreference> languages, int index)
15+
{
16+
var covered = new List<LanguagePreference>();
17+
if (!languages[index].IsFallback)
18+
{
19+
return covered;
20+
}
21+
22+
for (var i = index - 1; i >= 0; i--)
23+
{
24+
covered.Insert(0, languages[i]);
25+
if (!languages[i].IsFallback)
26+
{
27+
break;
28+
}
29+
}
30+
31+
return covered;
32+
}
33+
34+
// Where a drop on targetIndex lands when sourceIndex is dragged, or -1 for a
35+
// drop inside the dragged block itself. A fallback row may go anywhere,
36+
// joining a group is exactly what dropping into one means. An anchor block
37+
// snaps to group boundaries so it never splits another anchor from its
38+
// fallbacks: down-drags land after the target's whole group, up-drags before
39+
// its anchor.
40+
public static int DropTarget(this List<LanguagePreference> languages, int sourceIndex, int targetIndex)
41+
{
42+
if (targetIndex >= sourceIndex && targetIndex < sourceIndex + languages.GroupLength(sourceIndex))
43+
{
44+
return -1;
45+
}
46+
47+
if (languages[sourceIndex].IsFallback)
48+
{
49+
return targetIndex;
50+
}
51+
52+
var start = languages.GroupStart(targetIndex);
53+
return targetIndex > sourceIndex ? start + languages.GroupLength(start) - 1 : start;
54+
}
55+
56+
// Moves the row at sourceIndex, with its fallbacks if it is an anchor, to
57+
// where a drop on targetIndex lands.
58+
public static void Move(this List<LanguagePreference> languages, int sourceIndex, int targetIndex)
59+
{
60+
targetIndex = languages.DropTarget(sourceIndex, targetIndex);
61+
if (targetIndex < 0)
62+
{
63+
return;
64+
}
65+
66+
var target = languages[targetIndex];
67+
var length = languages.GroupLength(sourceIndex);
68+
69+
// A fallback dropped onto an anchor joins that anchor's group whichever
70+
// direction it came from; inserting above would hand it to the group before.
71+
var insertAfter = targetIndex > sourceIndex
72+
|| (languages[sourceIndex].IsFallback && !target.IsFallback);
73+
74+
var block = languages.GetRange(sourceIndex, length);
75+
languages.RemoveRange(sourceIndex, length);
76+
77+
var insertAt = languages.IndexOf(target);
78+
languages.InsertRange(insertAfter ? insertAt + 1 : insertAt, block);
79+
languages.AnchorTop();
80+
}
81+
82+
// Removing an anchor promotes its first fallback rather than silently
83+
// re-pointing it at whatever language happens to sit above the hole.
84+
public static void RemoveEntry(this List<LanguagePreference> languages, int index)
85+
{
86+
if (!languages[index].IsFallback && index + 1 < languages.Count)
87+
{
88+
languages[index + 1].IsFallback = false;
89+
}
90+
91+
languages.RemoveAt(index);
92+
}
93+
94+
// The top entry has nothing above it to stand in for.
95+
public static void AnchorTop(this List<LanguagePreference> languages)
96+
{
97+
if (languages.Count > 0)
98+
{
99+
languages[0].IsFallback = false;
100+
}
101+
}
102+
103+
// How many rows move together when this one is dragged: an anchor takes its
104+
// fallbacks along, since they only mean anything directly under it.
105+
private static int GroupLength(this List<LanguagePreference> languages, int index)
106+
{
107+
if (languages[index].IsFallback)
108+
{
109+
return 1;
110+
}
111+
112+
var length = 1;
113+
while (index + length < languages.Count && languages[index + length].IsFallback)
114+
{
115+
length++;
116+
}
117+
118+
return length;
119+
}
120+
121+
private static int GroupStart(this List<LanguagePreference> languages, int index)
122+
{
123+
while (index > 0 && languages[index].IsFallback)
124+
{
125+
index--;
126+
}
127+
128+
return index;
129+
}
130+
}

0 commit comments

Comments
 (0)