Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ public class ChartEditorDataModule : BaseDataModule
{
public CommandStack CommandStack { get; private set; } = null!;


public override void OnInit()
{
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#nullable enable

using System;
using CyanStars.Gameplay.ChartEditor.Model;
using ObservableCollections;
using R3;

namespace CyanStars.Gameplay.ChartEditor.Management
{
/// <summary>
/// 制谱器"是否存在未保存数据"的服务
/// </summary>
public class ChartEditorDirtyServer : IDisposable
{
private readonly CompositeDisposable Disposables = new CompositeDisposable();
private readonly ReactiveProperty<bool> hasUnsavedChanges = new ReactiveProperty<bool>(false);

/// <summary>
/// 是否存在未保存数据
/// </summary>
public ReadOnlyReactiveProperty<bool> HasUnsavedChanges => hasUnsavedChanges;


/// <summary>
/// 绑定到制谱器 Model
/// </summary>
/// <param name="model">制谱器 Model</param>
/// <param name="initialHasUnsavedChanges">会话初始是否存在未保存数据(新建谱面为 true,加载已有谱面为 false)</param>
public ChartEditorDirtyServer(ChartEditorModel model, bool initialHasUnsavedChanges)
{
hasUnsavedChanges.Value = initialHasUnsavedChanges;

GetDataChangeSource(model)
.Subscribe(_ => hasUnsavedChanges.Value = true)
.AddTo(Disposables);
}

/// <summary>
/// 标记为已保存(保存成功后调用)
/// </summary>
public void MarkSaved() => hasUnsavedChanges.Value = false;

public void Dispose() => Disposables.Dispose();


private static Observable<Unit> GetDataChangeSource(ChartEditorModel model)
{
var cp = model.ChartPackData.CurrentValue;
var cd = model.ChartData.CurrentValue;

return Observable.Merge(
cd.ReadyBeat.AsObservable().Skip(1).Select(_ => Unit.Default),
ToUnit(cd.SpeedGroupDatas),
ToUnit(cd.Notes),
ToUnit(cd.TrackDatas),
cp.DataVersion.AsObservable().Skip(1).Select(_ => Unit.Default),
cp.Title.AsObservable().Skip(1).Select(_ => Unit.Default),
cp.ChartPackInfo.AsObservable().Skip(1).Select(_ => Unit.Default),
cp.MusicPreviewStartBeat.AsObservable().Skip(1).Select(_ => Unit.Default),
cp.MusicPreviewEndBeat.AsObservable().Skip(1).Select(_ => Unit.Default),
cp.CoverFilePath.AsObservable().Skip(1).Select(_ => Unit.Default),
cp.CropStartPositionPercent.AsObservable().Skip(1).Select(_ => Unit.Default),
cp.CropHeightPercent.AsObservable().Skip(1).Select(_ => Unit.Default),
ToUnit(cp.MusicVersions),
ToUnit(cp.BpmGroup),
ToUnit(cp.ChartMetaDatas),

// 列表子项修改
model.BpmGroupDataChangedSubject.Select(_ => Unit.Default),
model.SelectedNoteDataChangedSubject.Select(_ => Unit.Default)
);
}

private static Observable<Unit> ToUnit<T>(ObservableList<T> list) =>
Observable.Merge(
list.ObserveAdd().Select(_ => Unit.Default),
list.ObserveRemove().Select(_ => Unit.Default),
list.ObserveReplace().Select(_ => Unit.Default),
list.ObserveReset().Select(_ => Unit.Default)
);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public void InitSceneRoot()
ChartPackData chartPackData;
ChartData chartData;

// 是否为新建谱面
bool isNewChart = false;

if (chartModule.SelectedRuntimeChartPack is null)
{
// 创建新谱包和谱面
Expand All @@ -77,6 +80,7 @@ public void InitSceneRoot()
chartPackData = new ChartPackData(randomName, bpmGroup: bpmGroup, chartMetaDatas: new List<ChartMetaData> { chartMetaData });

chartMetadataIndex = 0;
isNewChart = true;
}
else if (chartModule.ChartData is null)
{
Expand All @@ -92,6 +96,7 @@ public void InitSceneRoot()
chartPackData.ChartMetaDatas.Add(chartMetaData);

chartMetadataIndex = chartPackData.ChartMetaDatas.Count - 1;
isNewChart = true;
}
else
{
Expand All @@ -110,6 +115,7 @@ public void InitSceneRoot()
chartMetadataIndex,
chartPackData,
chartData,
isNewChart,
musicManager,
noteAudioManager,
shortcutManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public class MvvmBindManager : MonoBehaviour
{
private readonly CompositeDisposable Disposables = new CompositeDisposable();

private ChartEditorDirtyServer dirtyServer = null!;

[SerializeField]
private ToolbarView toolbarView = null!;

Expand Down Expand Up @@ -62,18 +64,21 @@ public class MvvmBindManager : MonoBehaviour
/// </summary>
/// <remarks>注意:由于引用关系,制谱器会修改传入的谱包和谱面实例内的数据。请先深拷贝一个谱包和谱面,再调用制谱器初始化</remarks>
public void StartBind(string workspacePath,
int chartMetadataIndex,
ChartPackData chartPackData,
ChartData chartData,
ChartEditorMusicManager musicManager,
ChartEditorNoteAudioManager chartEditorNoteAudioManager,
ShortcutManager shortcutManager,
ChartEditorPlayerPrefsManager playerPrefsManager)
int chartMetadataIndex,
ChartPackData chartPackData,
ChartData chartData,
bool initialHasUnsavedChanges,
ChartEditorMusicManager musicManager,
ChartEditorNoteAudioManager chartEditorNoteAudioManager,
ShortcutManager shortcutManager,
ChartEditorPlayerPrefsManager playerPrefsManager)
{
// TODO: 为 Model 实现 IDispose,以进一步管理生命周期
ChartEditorModel model =
var model =
new ChartEditorModel(workspacePath, chartMetadataIndex, chartPackData, chartData);

// 初始化脏状态服务,开始追踪未保存数据
dirtyServer = new ChartEditorDirtyServer(model, initialHasUnsavedChanges);

// 初始化一些 Manager
musicManager.Init(model);
chartEditorNoteAudioManager.Init(model);
Expand All @@ -84,7 +89,7 @@ public void StartBind(string workspacePath,
var toolbarViewModel = new ToolbarViewModel(model).AddTo(Disposables);
toolbarView.Bind(toolbarViewModel);

var menuButtonsViewModel = new MenuButtonsViewModel(model).AddTo(Disposables);
var menuButtonsViewModel = new MenuButtonsViewModel(model, dirtyServer).AddTo(Disposables);
menuButtonsView.Bind(menuButtonsViewModel);

var editorAttributeViewModel = new EditorAttributeViewModel(model).AddTo(Disposables);
Expand Down Expand Up @@ -122,6 +127,10 @@ public void StartBind(string workspacePath,
/// 退出制谱器时解除所有绑定,以释放内存
/// </summary>
/// <remarks>VM 通过 CatAsset 加载的资源也应该在此时由 VM 管理释放</remarks>
private void OnDestroy() => Disposables.Dispose();
private void OnDestroy()
{
Disposables.Dispose();
dirtyServer?.Dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System.Collections.Generic;
using CyanStars.Gameplay.ChartEditor.ViewModel;
using CyanStars.Utils.SelectableUI;
using DG.Tweening;
using R3;
using UnityEngine;
Expand Down Expand Up @@ -76,6 +77,7 @@ public class MenuButtonsView : BaseView<MenuButtonsViewModel>
private readonly List<ShortcutCommand.ListenerDisposable> ShortcutListeners = new();

private Canvas functionCanvas = null!;
private SelectableStateObserver? saveButtonSelectableStateObserver;


private void OnEnable()
Expand All @@ -90,6 +92,7 @@ public override void Bind(MenuButtonsViewModel targetViewModel)
base.Bind(targetViewModel);

functionCanvas = functionCanvasGroup.GetComponent<Canvas>();
saveButtonSelectableStateObserver = saveButton.GetComponent<SelectableStateObserver>();

FunctionCanvasVisibility
.Subscribe(isVisible =>
Expand Down Expand Up @@ -132,6 +135,15 @@ public override void Bind(MenuButtonsViewModel targetViewModel)
.OnClickAsObservable()
.Subscribe(_ => OnSaveRequested())
.AddTo(this);
ViewModel.HasUnsavedChanges
.Subscribe(hasUnsavedChanges =>
{
if (saveButtonSelectableStateObserver != null)
saveButtonSelectableStateObserver.SetInteractable(hasUnsavedChanges);
else
saveButton.interactable = hasUnsavedChanges;
})
.AddTo(this);
// testButton ...
undoButton
.OnClickAsObservable()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,36 @@ namespace CyanStars.Gameplay.ChartEditor.ViewModel
{
public class MenuButtonsViewModel : BaseViewModel
{
public MenuButtonsViewModel(ChartEditorModel model)
private readonly ChartEditorDirtyServer DirtyServer;

public MenuButtonsViewModel(ChartEditorModel model, ChartEditorDirtyServer dirtyServer)
: base(model)
{
DirtyServer = dirtyServer;
}

public void ExitChartEditor()
{
GameRoot.ChangeProcedure<MainHomeProcedure>();
}
public void ExitChartEditor() => GameRoot.ChangeProcedure<MainHomeProcedure>();

public void Undo()
{
base.CommandStack.Undo();
}
public void Undo() => base.CommandStack.Undo();
public void Redo() => base.CommandStack.Redo();

public void Redo()
{
base.CommandStack.Redo();
}
/// <summary>
/// 是否存在未保存数据
/// </summary>
public ReadOnlyReactiveProperty<bool> HasUnsavedChanges => DirtyServer.HasUnsavedChanges;

public void SaveFileToDisk()
{
ChartEditorFileManager.SaveChartAndAssetsToDisk(
bool isSaveSuccess = ChartEditorFileManager.SaveChartAndAssetsToDisk(
Model.WorkspacePath,
Model.ChartMetaDataIndex,
Model.ChartPackData.CurrentValue,
Model.ChartData.CurrentValue
);

// 只有保存成功才标记为已保存,失败保持未保存状态便于重试
if (isSaveSuccess)
DirtyServer.MarkSaved();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using CyanStars.Gameplay.ChartEditor.Command;
using CyanStars.Gameplay.ChartEditor.Model;
using R3;
using UnityEngine;

namespace CyanStars.Gameplay.ChartEditor.ViewModel
{
Expand Down Expand Up @@ -93,6 +94,9 @@ public void UpdateNoteJudgeBeat(string integerPart, string numerator, string den
return;
}

if (Model.SelectedNoteData.CurrentValue.JudgeBeat == newJudgeBeat)
return;

Beat oldJudgeBeat = Model.SelectedNoteData.CurrentValue.JudgeBeat;
var note = Model.SelectedNoteData.CurrentValue;
CommandStack.ExecuteCommand(
Expand Down Expand Up @@ -139,6 +143,9 @@ public void UpdateNoteEndJudgeBeat(string integerPart, string numerator, string
return;
}

if (note.EndJudgeBeat == newEndBeat)
return;

Beat oldEndBeat = note.EndJudgeBeat;
CommandStack.ExecuteCommand(
() =>
Expand Down Expand Up @@ -168,6 +175,9 @@ public void UpdateNotePos(string pos)
return;
}

if (Mathf.Approximately(((IChartNoteNormalPos)Model.SelectedNoteData.CurrentValue).Pos, newPosFloat))
return;

float oldPosFloat = ((IChartNoteNormalPos)Model.SelectedNoteData.CurrentValue).Pos;
var note = (IChartNoteNormalPos)Model.SelectedNoteData.CurrentValue;
CommandStack.ExecuteCommand(
Expand All @@ -192,6 +202,9 @@ public void UpdateBreakNotePos(BreakNotePos newBreakPos)
if (Model.SelectedNoteData.CurrentValue.Type != NoteType.Break)
throw new Exception("SelectedNoteData is not break");

if (((BreakChartNoteData)Model.SelectedNoteData.CurrentValue).BreakNotePos == newBreakPos)
return;

BreakNotePos oldBreakPos = ((BreakChartNoteData)Model.SelectedNoteData.CurrentValue).BreakNotePos;
var note = (BreakChartNoteData)Model.SelectedNoteData.CurrentValue;
CommandStack.ExecuteCommand(
Expand Down