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
5 changes: 5 additions & 0 deletions OpenUtau.Core/Util/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,11 @@ public class SerializablePreferences {
/// 自动保存间隔,单位秒(仅在启用时生效)。
/// </summary>
public int AutoSaveInterval = 120;
/// <summary>
/// Stop button behavior: 1 = StartTick -> SelectedPart -> 0 (default),
/// 2 = StartTick -> 0, 3 = Always 0.
/// </summary>
public int StopButtonBehavior = 1;
#endregion
}
}
Expand Down
15 changes: 15 additions & 0 deletions OpenUtauMobile/Assets/Lang/Strings.en.resx
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,21 @@
<data name="Settings.AutoScroll.Smooth" xml:space="preserve">
<value>Smooth Scrolling</value>
</data>
<data name="Settings.Edit.StopButtonBehavior" xml:space="preserve">
<value>Stop Button Behavior</value>
</data>
<data name="Settings.Edit.StopButtonBehavior.Desc" xml:space="preserve">
<value>Configure what happens when you press the stop button during and after playback.</value>
</data>
<data name="Settings.StopButton.StartTickSelectedPartZero" xml:space="preserve">
<value>Start Tick → Selected Part → 0 (Default)</value>
</data>
<data name="Settings.StopButton.StartTickZero" xml:space="preserve">
<value>Start Tick → 0</value>
</data>
<data name="Settings.StopButton.AlwaysZero" xml:space="preserve">
<value>Always Go to 0</value>
</data>
<data name="Settings.Render.CategoryTitle" xml:space="preserve">
<value>Render &amp; Performance</value>
</data>
Expand Down
15 changes: 15 additions & 0 deletions OpenUtauMobile/Assets/Lang/Strings.ja.resx
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,21 @@
<data name="Settings.AutoScroll.Smooth" xml:space="preserve">
<value>スムーズスクロール有効</value>
</data>
<data name="Settings.Edit.StopButtonBehavior" xml:space="preserve">
<value>停止ボタンの動作</value>
</data>
<data name="Settings.Edit.StopButtonBehavior.Desc" xml:space="preserve">
<value>再生中および再生後に停止ボタンを押したときの動作を設定します。</value>
</data>
<data name="Settings.StopButton.StartTickSelectedPartZero" xml:space="preserve">
<value>開始位置 → 選択部分 → 0(デフォルト)</value>
</data>
<data name="Settings.StopButton.StartTickZero" xml:space="preserve">
<value>開始位置 → 0</value>
</data>
<data name="Settings.StopButton.AlwaysZero" xml:space="preserve">
<value>常に 0 に移動</value>
</data>
<data name="Settings.Render.CategoryTitle" xml:space="preserve">
<value>レンダーとパフォーマンス</value>
</data>
Expand Down
15 changes: 15 additions & 0 deletions OpenUtauMobile/Assets/Lang/Strings.ru.resx
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,21 @@
<data name="Settings.AutoScroll.Smooth" xml:space="preserve">
<value>Плавная прокрутка</value>
</data>
<data name="Settings.Edit.StopButtonBehavior" xml:space="preserve">
<value>Поведение кнопки Стоп</value>
</data>
<data name="Settings.Edit.StopButtonBehavior.Desc" xml:space="preserve">
<value>Настройка поведения кнопки остановки во время и после воспроизведения.</value>
</data>
<data name="Settings.StopButton.StartTickSelectedPartZero" xml:space="preserve">
<value>Начало → Выделенная партия → 0 (По умолчанию)</value>
</data>
<data name="Settings.StopButton.StartTickZero" xml:space="preserve">
<value>Начало → 0</value>
</data>
<data name="Settings.StopButton.AlwaysZero" xml:space="preserve">
<value>Всегда к 0</value>
</data>
<data name="Settings.Render.CategoryTitle" xml:space="preserve">
<value>Рендеринг и производительность</value>
</data>
Expand Down
15 changes: 15 additions & 0 deletions OpenUtauMobile/Assets/Lang/Strings.uk.resx
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,21 @@
<data name="Settings.AutoScroll.Smooth" xml:space="preserve">
<value>Плавне прокручування</value>
</data>
<data name="Settings.Edit.StopButtonBehavior" xml:space="preserve">
<value>Поведінка кнопки Стоп</value>
</data>
<data name="Settings.Edit.StopButtonBehavior.Desc" xml:space="preserve">
<value>Налаштування поведінки кнопки зупинки під час та після відтворення.</value>
</data>
<data name="Settings.StopButton.StartTickSelectedPartZero" xml:space="preserve">
<value>Початок → Виділена партія → 0 (За замовчуванням)</value>
</data>
<data name="Settings.StopButton.StartTickZero" xml:space="preserve">
<value>Початок → 0</value>
</data>
<data name="Settings.StopButton.AlwaysZero" xml:space="preserve">
<value>Завжди до 0</value>
</data>
<data name="Settings.Render.CategoryTitle" xml:space="preserve">
<value>Рендеринг і продуктивність</value>
</data>
Expand Down
15 changes: 15 additions & 0 deletions OpenUtauMobile/Assets/Lang/Strings.zh-Hans.resx
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,21 @@
<data name="Settings.AutoScroll.Smooth" xml:space="preserve">
<value>启用平滑自动翻页</value>
</data>
<data name="Settings.Edit.StopButtonBehavior" xml:space="preserve">
<value>停止按钮行为</value>
</data>
<data name="Settings.Edit.StopButtonBehavior.Desc" xml:space="preserve">
<value>设置在播放期间和播放后按下停止按钮时的行为。</value>
</data>
<data name="Settings.StopButton.StartTickSelectedPartZero" xml:space="preserve">
<value>起始位置 → 选中部分 → 0(默认)</value>
</data>
<data name="Settings.StopButton.StartTickZero" xml:space="preserve">
<value>起始位置 → 0</value>
</data>
<data name="Settings.StopButton.AlwaysZero" xml:space="preserve">
<value>始终跳转到 0</value>
</data>
<data name="Settings.Render.CategoryTitle" xml:space="preserve">
<value>渲染与性能</value>
</data>
Expand Down
106 changes: 104 additions & 2 deletions OpenUtauMobile/ViewModels/EditorViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -226,6 +226,10 @@ public class EditorViewModel : NavigateViewModelBase, ICmdSubscriber, IDisposabl
// 最近一次来自回放流的等待标记(SetPlayPosTickNotification.waitingRendering)
private bool _streamWaitingRender;

private int _stopSeekState;
private bool _isSeekingInternally = false;
private bool _playheadMovedSinceStart = false;

// 随机数生成器
private Random Randomer { get; } = new();

Expand Down Expand Up @@ -254,13 +258,100 @@ public EditorViewModel(MainViewModel navigator, string path = "") : base(navigat
{
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(PlaybackManager.Inst.StartTick, true));
}

if (PlaybackManager.Inst.PlayingMaster || PlaybackManager.Inst.StartingToPlay)
{
_stopSeekState = 0;
}
}).DisposeWith(_disposables);
// 停止命令
StopCommand = ReactiveCommand.Create(() =>
{
bool wasPlaying = PlaybackManager.Inst.OutputActive || PlaybackManager.Inst.PlayingMaster;
PlaybackManager.Inst.StopPlayback();
SyncPlaybackStateFromAuthority();
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(0));

int mode = Preferences.Default.StopButtonBehavior;
if (mode <= 0 || mode > 3)
{
mode = 1;
}

if (mode == 3)
{
_isSeekingInternally = true;
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(0));
_stopSeekState = 3;
_playheadMovedSinceStart = false;
return;
}

if (wasPlaying)
{
_isSeekingInternally = true;
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(PlaybackManager.Inst.StartTick));
_stopSeekState = 1;
_playheadMovedSinceStart = false;
}
else
{
if (_stopSeekState == 0 && !_playheadMovedSinceStart)
{
_isSeekingInternally = true;
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(PlaybackManager.Inst.StartTick));
_stopSeekState = 1;
}
else
{
switch (mode)
{
case 2:
_isSeekingInternally = true;
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(0));
_stopSeekState = 3;
break;
case 3:
_isSeekingInternally = true;
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(0));
break;
default:
if (_stopSeekState <= 1)
{
int? selectedTick = null;
if (SelectedParts is { Count: > 0 } parts)
{
selectedTick = parts.Min(p => p.position);
}

if (selectedTick.HasValue && selectedTick.Value != PlayPosTick)
{
_isSeekingInternally = true;
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(selectedTick.Value));
_stopSeekState = 2;
}
else
{
_isSeekingInternally = true;
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(0));
_stopSeekState = 3;
}
}
else if (_stopSeekState == 2)
{
_isSeekingInternally = true;
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(0));
_stopSeekState = 3;
}
else
{
_isSeekingInternally = true;
DocManager.Inst.ExecuteCmd(new SeekPlayPosTickNotification(0));
_stopSeekState = 3;
}
break;
}
}
}
}).DisposeWith(_disposables);
// 工程信息编辑命令
EditBpmCommand = ReactiveCommand.CreateFromTask(EditBpmAsync);
Expand Down Expand Up @@ -392,6 +483,17 @@ public void OnNext(UCommand cmd, bool isUndo)
// TODO: 一定要发到UI线程吗?:不需要!docman已经做了
switch (cmd)
{
case SeekPlayPosTickNotification _:
if (_isSeekingInternally)
{
_isSeekingInternally = false;
}
else
{
_playheadMovedSinceStart = true;
_stopSeekState = 0;
}
break;
case SetPlayPosTickNotification setPlayPos:
// 等待渲染期间冻结回放标记,避免位置在等待点附近抖动漂移。
if (!setPlayPos.waitingRendering)
Expand Down
52 changes: 52 additions & 0 deletions OpenUtauMobile/ViewModels/SettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,25 @@ public PianoKeyBehaviorOption(PianoKeyBehavior value, string displayName)
}
}

public enum StopButtonBehavior
{
StartTickSelectedPartZero = 1,
StartTickZero = 2,
AlwaysZero = 3
}

public class StopButtonBehaviorOption
{
public StopButtonBehavior Value { get; }
public string DisplayName { get; }

public StopButtonBehaviorOption(StopButtonBehavior value, string displayName)
{
Value = value;
DisplayName = displayName;
}
}

/// <summary>走带自动翻页行为选项(用于绑定到选择器)。</summary>
public class AutoScrollBehaviorOption
{
Expand Down Expand Up @@ -328,6 +347,20 @@ public class SettingsViewModel : NavigateViewModelBase, IDisposable
[Reactive]
public AutoScrollBehaviorOption? SelectedAutoScrollBehavior { get; set; }

// ── Edit & Behaviour: Stop Button ──────────────────────────────
/// <summary>List of available stop button actions.</summary>
public IReadOnlyList<StopButtonBehaviorOption> AvailableStopButtonBehaviors { get; } =
new List<StopButtonBehaviorOption>
{
new(StopButtonBehavior.StartTickSelectedPartZero, L.S("Settings.StopButton.StartTickSelectedPartZero")),
new(StopButtonBehavior.StartTickZero, L.S("Settings.StopButton.StartTickZero")),
new(StopButtonBehavior.AlwaysZero, L.S("Settings.StopButton.AlwaysZero"))
};

/// <summary>The currently selected behavior option for the Stop button.</summary>
[Reactive]
public StopButtonBehaviorOption? SelectedStopButtonBehavior { get; set; }

/// <summary>回放刷新率(1-60)。</summary>
[Reactive]
public int PlaybackRefreshRate { get; set; }
Expand Down Expand Up @@ -551,6 +584,25 @@ await Task.Run(() =>
})
.DisposeWith(_disposables);

// Initializing Stop Button Behavior
int stopBehaviorVal = Preferences.Default.StopButtonBehavior;
StopButtonBehavior stopBehavior = stopBehaviorVal is >= 1 and <= 3
? (StopButtonBehavior)stopBehaviorVal
: StopButtonBehavior.StartTickSelectedPartZero;
SelectedStopButtonBehavior = AvailableStopButtonBehaviors.FirstOrDefault(b => b.Value == stopBehavior)
?? AvailableStopButtonBehaviors[0];

// Monitor changes in the behavior of the Stop button and persist them to Preferences
this.WhenAnyValue(x => x.SelectedStopButtonBehavior)
.Skip(1)
.WhereNotNull()
.Subscribe(opt =>
{
Preferences.Default.StopButtonBehavior = (int)opt.Value;
Preferences.Save();
})
.DisposeWith(_disposables);

// 编辑与行为:回放刷新率 / 立绘开关 / 撤销上限
PlaybackRefreshRate = Math.Clamp((int)Math.Round(Preferences.Default.PlaybackRefreshRate), 1, 60);
ShowPortraitEnabled = Preferences.Default.ShowPortrait;
Expand Down
Loading