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
30 changes: 18 additions & 12 deletions OpenUtau/Controls/NotePropertyExpression.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:OpenUtau.App.ViewModels"
x:Class="OpenUtau.App.Controls.NotePropertyExpression">
<Grid ColumnDefinitions="143,7,50,20,*">
<Label Content="{Binding Name}" Grid.Column="0" VerticalAlignment="Center" FontWeight="{Binding NameFontWeight}"/>
<TextBox Text="{Binding Value, Mode=OneWay}" Grid.Column="2" IsVisible="{Binding IsNumerical}" VerticalAlignment="Center" IsEnabled="{Binding IsNoteSelected}"
GotFocus="OnTextBoxGotFocus" LostFocus="OnTextBoxLostFocus"/>
<Slider Grid.Column="4" Classes="fader" Value="{Binding Value}" Minimum="{Binding Min}" Maximum="{Binding Max}"
TickPlacement="BottomRight" TickFrequency="1" IsSnapToTickEnabled="true" IsVisible="{Binding IsNumerical}" VerticalAlignment="Center" IsEnabled="{Binding IsNoteSelected}"
Name="slider"/>
<ComboBox Grid.Column="1" Grid.ColumnSpan="4" ItemsSource="{Binding Options}"
SelectedIndex="{Binding SelectedOption}" MinWidth="120" IsVisible="{Binding IsOptions}" VerticalAlignment="Center" IsEnabled="{Binding IsNoteSelected}"
IsDropDownOpen="{Binding DropDownOpen, Mode=OneWayToSource}"
Name="comboBox"/>
</Grid>
<StackPanel>
<Grid ColumnDefinitions="143,7,50,20,*">
<Label Content="{Binding Name}" Grid.Column="0" VerticalAlignment="Center" FontWeight="{Binding NameFontWeight}"/>
<TextBox Text="{Binding Value, Mode=OneWay}" Grid.Column="2" IsVisible="{Binding IsNumerical}" VerticalAlignment="Center" IsEnabled="{Binding IsNoteSelected}"
GotFocus="OnTextBoxGotFocus" LostFocus="OnTextBoxLostFocus"/>
<Slider Grid.Column="4" Classes="fader" Value="{Binding Value}" Minimum="{Binding Min}" Maximum="{Binding Max}"
TickPlacement="BottomRight" TickFrequency="1" IsSnapToTickEnabled="true" IsVisible="{Binding IsNumerical}" VerticalAlignment="Center" IsEnabled="{Binding IsNoteSelected}"
Name="slider"/>
<ComboBox Grid.Column="1" Grid.ColumnSpan="4" ItemsSource="{Binding Options}"
SelectedIndex="{Binding SelectedOption}" MinWidth="120" IsVisible="{Binding IsOptions}" VerticalAlignment="Center" IsEnabled="{Binding IsNoteSelected}"
IsDropDownOpen="{Binding DropDownOpen, Mode=OneWayToSource}"
Name="comboBox"/>
<TextBox Text="{Binding FlagValue, Mode=OneWay}" Grid.Column="1" Grid.ColumnSpan="4" IsVisible="{Binding IsFlagBox}" VerticalAlignment="Center" IsEnabled="{Binding IsNoteSelected}"
GotFocus="OnTextBoxGotFocus" LostFocus="OnFlagBoxLostFocus"/>
</Grid>
<TextBlock Text="{Binding Warning}" Margin="3,0" TextWrapping="Wrap" FontSize="11" Foreground="Red"
IsVisible="{Binding Warning, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
</UserControl>
18 changes: 18 additions & 0 deletions OpenUtau/Controls/NotePropertyExpression.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
using OpenUtau.App.ViewModels;
using OpenUtau.Core;
using Serilog;
Expand Down Expand Up @@ -31,6 +32,23 @@ void OnTextBoxLostFocus(object? sender, RoutedEventArgs args) {
SetNumericalExpressions(textBox.Text);
}
}
void OnFlagBoxLostFocus(object? sender, RoutedEventArgs args) {
Log.Debug("Note property textbox lost focus");
if (sender is TextBox textBox && textBoxValue != textBox.Text) {
if (DataContext is NotePropertyExpViewModel viewModel) {
NotePropertiesViewModel.PanelControlPressed = true;
viewModel.SetFlagFromText(textBox.Text);
NotePropertiesViewModel.PanelControlPressed = false;

if (!string.IsNullOrEmpty(viewModel.Warning)) {
var scrollViewer = this.FindAncestorOfType<ScrollViewer>();
if (scrollViewer != null) {
scrollViewer.ScrollToEnd();
}
}
}
}
}

// slider
void SliderPointerPressed(object? sender, PointerPressedEventArgs args) {
Expand Down
1 change: 1 addition & 0 deletions OpenUtau/Strings/Strings.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ Do you want to continue by splitting at the nearest position after current playh
<system:String x:Key="errors.failed.openfile">Failed to open</system:String>
<system:String x:Key="errors.failed.openlocation">Failed to open location</system:String>
<system:String x:Key="errors.failed.opennewerproject">Project file is newer than software! Upgrade OpenUtau!</system:String>
<system:String x:Key="errors.failed.parseflag">Failed to parse the flag. Please check the Expression settings: {0}</system:String>
<system:String x:Key="errors.failed.render">Failed to render.</system:String>
<system:String x:Key="errors.failed.runeditingmacro">Failed to run editing macro</system:String>
<system:String x:Key="errors.failed.save">Failed to save</system:String>
Expand Down
124 changes: 122 additions & 2 deletions OpenUtau/ViewModels/NotePropertiesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using Avalonia.Media;
using OpenUtau.Classic;
using OpenUtau.Classic.Flags;
using OpenUtau.Core;
using OpenUtau.Core.Format;
using OpenUtau.Core.Render;
using OpenUtau.Core.Ustx;
using OpenUtau.Core.Util;
using ReactiveUI;
Expand Down Expand Up @@ -194,6 +198,10 @@ public void LoadPart(UPart? part) {
Expressions.Add(viewModel);
}
}
if (track.RendererSettings.renderer == Renderers.CLASSIC) {
var viewModel = new NotePropertyExpViewModel(this); // FlagBox
Expressions.Add(viewModel);
}
AttachExpressions();
} else {
this.Part = null;
Expand All @@ -204,9 +212,21 @@ private void AttachExpressions() {
if (Expressions.Count > 0) {
if (selectedNotes.Count > 0) {
var note = selectedNotes.First();

foreach (NotePropertyExpViewModel exp in Expressions) {
exp.IsNoteSelected = true;

if (exp.IsFlagBox) {
var phoneme = Part?.phonemes.FirstOrDefault(phoneme => phoneme.Parent == note);
if (phoneme != null) {
exp.FlagValue = string.Empty; // Assign a different value just in case the text box is empty
exp.FlagValue = GetFlagText(phoneme);
} else {
exp.FlagValue = string.Empty;
}
exp.Warning = string.Empty;
continue;
}

var phonemeExpression = note.phonemeExpressions.FirstOrDefault(e => e.abbr == exp.abbr && e.index == 0);
if (phonemeExpression != null) {
if (exp.IsNumerical) {
Expand All @@ -217,6 +237,7 @@ private void AttachExpressions() {
exp.HasValue = true;
} else {
if (exp.IsNumerical) {
exp.Value = exp.defaultValue + 1; // Assign a different value just in case the text box is empty
exp.Value = exp.defaultValue;
} else if (exp.IsOptions) {
exp.SelectedOption = (int)exp.defaultValue;
Expand All @@ -234,15 +255,41 @@ private void AttachExpressions() {
exp.IsNoteSelected = false;
exp.HasValue = false;
if (exp.IsNumerical) {
exp.Value = exp.defaultValue + 1;
exp.Value = exp.defaultValue;
} else if (exp.IsOptions) {
exp.SelectedOption = (int)exp.defaultValue;
} else if (exp.IsFlagBox) {
exp.FlagValue = string.Empty;
exp.Warning = string.Empty;
}
}
}
}
}

private string GetFlagText(UPhoneme phoneme) {
if (Part == null) {
return string.Empty;
}
var track = DocManager.Inst.Project.tracks[Part.trackNo];
if (track.RendererSettings.renderer != Renderers.CLASSIC) {
return string.Empty;
}

var resampler = ToolsManager.Inst.GetResampler(Renderers.CLASSIC);
var flags = phoneme.GetResamplerFlags(DocManager.Inst.Project, track)
.Where(flag => flag.Item3 != null && resampler.SupportsFlag(flag.Item3));
var builder = new StringBuilder();
foreach (var flag in flags) {
builder.Append(flag.Item1);
if (flag.Item2.HasValue) {
builder.Append(flag.Item2.Value);
}
}
return builder.ToString();
}

#region ICmdSubscriber
public void OnNext(UCommand cmd, bool isUndo) {
var note = selectedNotes.FirstOrDefault();
Expand Down Expand Up @@ -541,6 +588,63 @@ public void SetOptionalExpressionsChanges(string abbr, int? value) {
DocManager.Inst.EndUndoGroup();
}
}
public void SetFlagFromText(string? text, out string? warning) {
warning = null;
if (AllowNoteEdit && Part != null && selectedNotes.Count > 0) {
var dict = new Dictionary<string, float>();
if (!string.IsNullOrWhiteSpace(text)) {
var parser = new UstFlagParser();
foreach (UstFlag flag in parser.Parse(text)) {
dict.Add(flag.Key, flag.Value);
}
}

var track = DocManager.Inst.Project.tracks[Part.trackNo];
DocManager.Inst.StartUndoGroup("command.property.edit");
track.GetSupportedExps(DocManager.Inst.Project)
.Where(d => d.isFlag && d.type == UExpressionType.Numerical)
.ForEach(descriptor => {
if (dict.TryGetValue(descriptor.flag, out float value)) {
dict.Remove(descriptor.flag);
if (value != descriptor.CustomDefaultValue) {
value = float.Clamp(value, descriptor.min, descriptor.max);
DocManager.Inst.ExecuteCmd(new SetNotesSameExpressionCommand(DocManager.Inst.Project, track, Part, selectedNotes, descriptor.abbr, value));
} else {
DocManager.Inst.ExecuteCmd(new SetNotesSameExpressionCommand(DocManager.Inst.Project, track, Part, selectedNotes, descriptor.abbr, null));
}
} else {
DocManager.Inst.ExecuteCmd(new SetNotesSameExpressionCommand(DocManager.Inst.Project, track, Part, selectedNotes, descriptor.abbr, null));
}
});
track.GetSupportedExps(DocManager.Inst.Project)
.Where(d => d.isFlag && d.type == UExpressionType.Options)
.ForEach(descriptor => {
bool find = false;
for (int i = 0; i < descriptor.options.Length; i++) {
string option = descriptor.options[i];
var flag = dict.FirstOrDefault(flag => option == $"{flag.Key}{flag.Value}" || option == $"{flag.Key}");
if (!string.IsNullOrEmpty(flag.Key)) {
dict.Remove(flag.Key);
find = true;
if (i != descriptor.CustomDefaultValue) {
DocManager.Inst.ExecuteCmd(new SetNotesSameExpressionCommand(DocManager.Inst.Project, track, Part, selectedNotes, descriptor.abbr, i));
} else {
DocManager.Inst.ExecuteCmd(new SetNotesSameExpressionCommand(DocManager.Inst.Project, track, Part, selectedNotes, descriptor.abbr, null));
}
break;
}
}
if (!find) {
DocManager.Inst.ExecuteCmd(new SetNotesSameExpressionCommand(DocManager.Inst.Project, track, Part, selectedNotes, descriptor.abbr, null));
}
});
if (dict.Count > 0) {
ThemeManager.TryGetString("errors.failed.parseflag", out string str);
warning = string.Format(str, string.Join(", ", dict.Keys));
}
DocManager.Inst.EndUndoGroup();
}
}

// presets
public void SavePortamentoPreset(string name) {
Expand Down Expand Up @@ -581,6 +685,7 @@ public class NotePropertyExpViewModel : ViewModelBase {
public string Name { get; set; }
public bool IsNumerical { get; set; } = false;
public bool IsOptions { get; set; } = false;
public bool IsFlagBox { get; set; } = false;
public float Min { get; set; }
public float Max { get; set; }
public ObservableCollection<string> Options { get; set; } = new ObservableCollection<string>();
Expand All @@ -589,10 +694,12 @@ public class NotePropertyExpViewModel : ViewModelBase {

[Reactive] public bool IsNoteSelected { get; set; } = false;
[Reactive] public float Value { get; set; }
[Reactive] public string FlagValue { get; set; } = string.Empty;
[Reactive] public int SelectedOption { get; set; }
[Reactive] public bool DropDownOpen { get; set; }
[Reactive] public bool HasValue { get; set; } = false;
[Reactive] public FontWeight NameFontWeight { get; set; }
[Reactive] public string Warning { get; set; } = string.Empty;

private NotePropertiesViewModel parentViewmodel;

Expand Down Expand Up @@ -631,6 +738,15 @@ public NotePropertyExpViewModel(UExpressionDescriptor descriptor, NoteProperties
}
});
}
// Flag text box
public NotePropertyExpViewModel(NotePropertiesViewModel parent) {
Name = "Flags";
defaultValue = 0;
abbr = string.Empty;
IsFlagBox = true;
parentViewmodel = parent;
NameFontWeight = FontWeight.Normal;
}

public void SetNumericalExpressions(object? obj) {
float? value = null;
Expand All @@ -641,7 +757,11 @@ public void SetNumericalExpressions(object? obj) {
value = f;
}
parentViewmodel.SetNumericalExpressionsChanges(abbr, value);
this.RaisePropertyChanged(nameof(Value));
}

public void SetFlagFromText(string? text) {
parentViewmodel.SetFlagFromText(text, out string? warning);
Warning = warning ?? string.Empty;
}

public override string ToString() {
Expand Down
Loading