fix(persist): one shared z-order across Source + WebcamSceneConfig rows
A webcam dragged between sources reverted to the bottom of the stack on every relaunch: Source and WebcamSceneConfig each carried independent per-type SortOrder counters, and Load appended all Sources before all configs. Save now stamps both tables' SortOrder from the element's index within scene.Elements; Load merges the two tables' rows by that shared z (sources-first tie-break preserves legacy rows). Cross-type reorder now survives a fresh LayoutStore reload. Task 37 queued: defaults vs current layout split (creator directive) — capture out-of-scope work in TASKS.md rather than folding it in.
This commit is contained in:
@@ -11,8 +11,8 @@ public partial class LayoutStore : IDisposable
|
||||
Socials = null;
|
||||
Music = null;
|
||||
var scenes = new List<Scene>();
|
||||
var sourcesByScene = new Dictionary<string, List<Source>>();
|
||||
var configsByScene = new Dictionary<string, List<WebcamSceneConfig>>();
|
||||
var sourcesByScene = new Dictionary<string, List<(Source Source, int Sort)>>();
|
||||
var configsByScene = new Dictionary<string, List<(WebcamSceneConfig Config, int Sort)>>();
|
||||
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
@@ -99,7 +99,7 @@ public partial class LayoutStore : IDisposable
|
||||
IsBackground, CaptureKey, ShowDesktop,
|
||||
ChatFontSize, ChatFontColor, ChatBackgroundColor, ChatMaxMessages,
|
||||
ChatShowTimestamps, ChatShowBadges, ChatMemberColor, ChatSuperChatColor,
|
||||
WebUri, MediaPath, MediaIsLooping, MediaVolume, MediaPlaybackState
|
||||
WebUri, MediaPath, MediaIsLooping, MediaVolume, MediaPlaybackState, SortOrder
|
||||
FROM Source ORDER BY SortOrder
|
||||
""";
|
||||
using var reader = cmd.ExecuteReader();
|
||||
@@ -147,8 +147,8 @@ public partial class LayoutStore : IDisposable
|
||||
: MediaPlaybackState.Stopped,
|
||||
};
|
||||
if (!sourcesByScene.TryGetValue(sceneId, out var list))
|
||||
sourcesByScene[sceneId] = list = new List<Source>();
|
||||
list.Add(source);
|
||||
sourcesByScene[sceneId] = list = new List<(Source, int)>();
|
||||
list.Add((source, reader.GetInt32(30)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ public partial class LayoutStore : IDisposable
|
||||
cmd.CommandText = """
|
||||
SELECT SceneId, WebcamId, IsVisible, X, Y, Width, Height, Opacity,
|
||||
ClipShape, IsMirrored, BorderColor, BorderOpacity, BorderWidth, BorderAnimation,
|
||||
RectWidth, RectHeight
|
||||
RectWidth, RectHeight, SortOrder
|
||||
FROM WebcamSceneConfig ORDER BY SortOrder
|
||||
""";
|
||||
using var reader = cmd.ExecuteReader();
|
||||
@@ -184,19 +184,30 @@ public partial class LayoutStore : IDisposable
|
||||
RectHeight = reader.IsDBNull(15) ? null : reader.GetDouble(15),
|
||||
};
|
||||
if (!configsByScene.TryGetValue(sceneId, out var list))
|
||||
configsByScene[sceneId] = list = new List<WebcamSceneConfig>();
|
||||
list.Add(config);
|
||||
configsByScene[sceneId] = list = new List<(WebcamSceneConfig, int)>();
|
||||
list.Add((config, reader.GetInt32(16)));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var scene in scenes)
|
||||
{
|
||||
// Source and WebcamSceneConfig rows share ONE z-order space per scene
|
||||
// (both SortOrders = the element's index within scene.Elements). Merge
|
||||
// the two tables' rows by that shared z rather than appending all
|
||||
// sources before all configs — a webcam slotted between sources must
|
||||
// reload in place. Legacy rows (written before the unified space) may
|
||||
// tie at 0: break ties sources-first, so old data reads back stacked
|
||||
// the same way it did pre-unification.
|
||||
var ordered = new List<(int Sort, SceneElement Element)>();
|
||||
if (sourcesByScene.TryGetValue(scene.Id, out var sources))
|
||||
foreach (var source in sources)
|
||||
scene.Elements.Add(source);
|
||||
foreach (var (source, sort) in sources)
|
||||
ordered.Add((sort, source));
|
||||
if (configsByScene.TryGetValue(scene.Id, out var configs))
|
||||
foreach (var config in configs)
|
||||
scene.Elements.Add(config);
|
||||
foreach (var (config, sort) in configs)
|
||||
ordered.Add((sort, config));
|
||||
|
||||
foreach (var (_, element) in ordered.OrderBy(e => e.Sort).ThenBy(e => e.Element is Source ? 0 : 1))
|
||||
scene.Elements.Add(element);
|
||||
|
||||
var bg = scene.Elements.OfType<Source>().FirstOrDefault(s => s.IsBackground);
|
||||
if (bg != null)
|
||||
|
||||
@@ -129,12 +129,16 @@ public partial class LayoutStore : IDisposable
|
||||
var mediaVolumeP = cmd.Parameters.Add("$mediaVolume", SqliteType.Real);
|
||||
var mediaPlaybackStateP = cmd.Parameters.Add("$mediaPlaybackState", SqliteType.Text);
|
||||
|
||||
// Source and WebcamSceneConfig share ONE z-order space per scene:
|
||||
// each element gets its index within scene.Elements regardless of
|
||||
// table, so a webcam slotted between sources survives save→load.
|
||||
// (Pre-unified rows tie at 0 — load breaks ties sources-first.)
|
||||
foreach (var scene in scenes)
|
||||
{
|
||||
var sort = 0;
|
||||
var z = 0;
|
||||
foreach (var element in scene.Elements)
|
||||
{
|
||||
if (element is not Source source) continue;
|
||||
if (element is not Source source) { z++; continue; }
|
||||
idP.Value = source.Id;
|
||||
sceneIdP.Value = scene.Id;
|
||||
assetIdP.Value = (object?)source.AssetId ?? DBNull.Value;
|
||||
@@ -152,7 +156,7 @@ public partial class LayoutStore : IDisposable
|
||||
isBackgroundP.Value = source.IsBackground ? 1 : 0;
|
||||
captureKeyP.Value = (object?)source.CaptureKey ?? DBNull.Value;
|
||||
showDesktopP.Value = source.ShowDesktop ? 1 : 0;
|
||||
sortP.Value = sort++;
|
||||
sortP.Value = z++;
|
||||
chatFontSizeP.Value = source.ChatFontSize;
|
||||
chatFontColorP.Value = source.ChatFontColor;
|
||||
chatBgColorP.Value = source.ChatBackgroundColor;
|
||||
@@ -215,9 +219,10 @@ public partial class LayoutStore : IDisposable
|
||||
|
||||
foreach (var scene in scenes)
|
||||
{
|
||||
var sort = 0;
|
||||
foreach (var config in scene.Elements.OfType<WebcamSceneConfig>())
|
||||
var z = 0;
|
||||
foreach (var element in scene.Elements)
|
||||
{
|
||||
if (element is not WebcamSceneConfig config) { z++; continue; }
|
||||
sceneIdP.Value = scene.Id;
|
||||
webcamIdP.Value = config.WebcamId;
|
||||
visibleP.Value = config.IsVisible ? 1 : 0;
|
||||
@@ -234,7 +239,7 @@ public partial class LayoutStore : IDisposable
|
||||
animationP.Value = config.BorderAnimation.ToString();
|
||||
rectWidthP.Value = (object?)config.RectWidth ?? DBNull.Value;
|
||||
rectHeightP.Value = (object?)config.RectHeight ?? DBNull.Value;
|
||||
sortP.Value = sort++;
|
||||
sortP.Value = z++;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user