@pro-gram/flux 0.0.0-stage → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/NOTICE.txt +32 -0
- package/README.md +102 -2
- package/bin/flux.js +29 -0
- package/dependencies.json +14 -0
- package/package.json +60 -6
- package/payload/DrumBuddy-Setup.exe +0 -0
- package/scripts/install.js +58 -0
- package/scripts/version.js +12 -0
- package/source/AssetBootstrap.cs +49 -0
- package/source/Audio.cs +453 -0
- package/source/AudioDeviceSettings.cs +126 -0
- package/source/AudioInputs.cs +50 -0
- package/source/AudioOutputs.cs +327 -0
- package/source/AudioWorkspace.cs +71 -0
- package/source/BrandArtwork.cs +71 -0
- package/source/Build Instructions.txt +59 -0
- package/source/Build.ps1 +123 -0
- package/source/ControlMode.cs +687 -0
- package/source/ControlTests.cs +88 -0
- package/source/ConversionFormats.cs +59 -0
- package/source/ConversionTests.cs +66 -0
- package/source/DockablePane.cs +95 -0
- package/source/DriverSetup.cs +28 -0
- package/source/EditorView.cs +626 -0
- package/source/Fetch-Dependencies.ps1 +15 -0
- package/source/FloatRecording.cs +99 -0
- package/source/FloatRecordingTests.cs +170 -0
- package/source/Flux-Mark.svg +49 -0
- package/source/FluxTempo Logo Prompt.txt +3 -0
- package/source/FluxTempo-Mark.svg +49 -0
- package/source/HomeView.cs +32 -0
- package/source/InterfaceSettings.cs +219 -0
- package/source/InterfaceTests.cs +166 -0
- package/source/LICENSE.txt +23 -0
- package/source/Logo Prompt.txt +16 -0
- package/source/Main.cs +781 -0
- package/source/Main.xaml +292 -0
- package/source/MediaPlayerView.cs +274 -0
- package/source/MidiControl.cs +113 -0
- package/source/MidiPerformance.cs +125 -0
- package/source/Mixer.cs +280 -0
- package/source/NativeMemory.cs +165 -0
- package/source/PanelFeedback.cs +75 -0
- package/source/PlaybackTests.cs +134 -0
- package/source/Quick Start.txt +77 -0
- package/source/SelfTest.cs +78 -0
- package/source/SettingsWindow.cs +208 -0
- package/source/Setup.cs +192 -0
- package/source/Setup.xaml +28 -0
- package/source/ShellStyles.xaml +99 -0
- package/source/TempoGrid.cs +95 -0
- package/source/TempoGridTests.cs +108 -0
- package/source/Third Party Notices.txt +32 -0
- package/source/Timeline.cs +261 -0
- package/source/TimelineGestures.cs +57 -0
- package/source/Uninstall.cs +69 -0
- package/source/WorkspaceTests.cs +198 -0
- package/source/app.manifest +7 -0
- package/source/brand-archive/Drum-Buddy-Black-Hair.png +0 -0
- package/source/dependency-lock.json +62 -0
- package/source/fluxtempo-mark.png +0 -0
- package/source/logo.png +0 -0
- package/source/r16-panel.png +0 -0
- package/source/references/R16 Format Research.txt +16 -0
- package/test/package.test.js +55 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.IO;
|
|
3
|
+
using System.Linq;
|
|
4
|
+
using System.Collections.Generic;
|
|
5
|
+
using System.Windows;
|
|
6
|
+
using System.Windows.Controls;
|
|
7
|
+
using System.Windows.Input;
|
|
8
|
+
using NAudio.Wave;
|
|
9
|
+
|
|
10
|
+
namespace DrumBuddy
|
|
11
|
+
{
|
|
12
|
+
public sealed class SettingsWindow : Window
|
|
13
|
+
{
|
|
14
|
+
private readonly InterfaceSettings draft;
|
|
15
|
+
private string saved;
|
|
16
|
+
private readonly ContentControl body;
|
|
17
|
+
private readonly TextBlock status;
|
|
18
|
+
private readonly Button save;
|
|
19
|
+
private readonly bool audioLocked;
|
|
20
|
+
private TextBox capture;
|
|
21
|
+
private Button captureButton;
|
|
22
|
+
private ShortcutBinding captureBinding;
|
|
23
|
+
private PlaybackRoute testOutput;
|
|
24
|
+
private readonly Action<InterfaceSettings> persist;
|
|
25
|
+
public bool HasUnsavedChanges { get { return draft.Signature() != saved; } }
|
|
26
|
+
public SettingsWindow(Window owner, string category, bool audioLocked, Action<InterfaceSettings> persist = null, InterfaceSettings initial = null)
|
|
27
|
+
{
|
|
28
|
+
this.persist = persist ?? InterfacePreferences.Save;
|
|
29
|
+
Owner = owner; this.audioLocked = audioLocked; Title = "Flux - Settings";
|
|
30
|
+
Width = 930; Height = Math.Min(700, SystemParameters.WorkArea.Height - 40); MinWidth = 700; MinHeight = 460;
|
|
31
|
+
FontFamily = owner == null ? new System.Windows.Media.FontFamily("Segoe UI") : owner.FontFamily; FontSize = 13; WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
|
32
|
+
ThemePalette.Install(this); draft = (initial ?? InterfacePreferences.Current).Copy(); saved = draft.Signature();
|
|
33
|
+
var root = new Grid { Margin = new Thickness(16) }; Content = root;
|
|
34
|
+
root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); root.RowDefinitions.Add(new RowDefinition()); root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
|
|
35
|
+
root.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(166) }); root.ColumnDefinitions.Add(new ColumnDefinition());
|
|
36
|
+
var title = WorkspaceUi.Text("Settings", 25); title.Margin = new Thickness(8, 0, 0, 18); Grid.SetColumnSpan(title, 2); root.Children.Add(title);
|
|
37
|
+
var navigation = new ListBox { ItemsSource = new[] { "General", "Timeline", "Audio", "Keybinds", "About" }, FontSize = 14, Background = ThemePalette.Brush("#F3F5F5"), Foreground = ThemePalette.Brush("#182C37"), BorderThickness = new Thickness(0), Padding = new Thickness(4), Margin = new Thickness(0, 0, 16, 0) };
|
|
38
|
+
Grid.SetRow(navigation, 1); root.Children.Add(navigation);
|
|
39
|
+
body = new ContentControl(); Grid.SetRow(body, 1); Grid.SetColumn(body, 1); root.Children.Add(body);
|
|
40
|
+
var footer = new Grid { Margin = new Thickness(4, 14, 0, 0) }; footer.ColumnDefinitions.Add(new ColumnDefinition()); footer.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); footer.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
|
41
|
+
Grid.SetRow(footer, 2); Grid.SetColumnSpan(footer, 2); root.Children.Add(footer);
|
|
42
|
+
status = WorkspaceUi.Text("Settings are saved only when you click Save.", 13); footer.Children.Add(status);
|
|
43
|
+
save = WorkspaceUi.Button("Save", Save); save.MinWidth = 82; Grid.SetColumn(save, 1); footer.Children.Add(save);
|
|
44
|
+
var close = WorkspaceUi.Button("Close", Close); close.MinWidth = 76; Grid.SetColumn(close, 2); footer.Children.Add(close);
|
|
45
|
+
navigation.SelectionChanged += delegate { CancelCapture(); StopTest(); ShowCategory((string)navigation.SelectedItem); };
|
|
46
|
+
navigation.SelectedItem = category == "timeline" ? "Timeline" : category == "audio" ? "Audio" : category == "keybinds" ? "Keybinds" : category == "about" ? "About" : "General";
|
|
47
|
+
PreviewKeyDown += OnKey;
|
|
48
|
+
Closing += delegate(object sender, System.ComponentModel.CancelEventArgs e) {
|
|
49
|
+
CancelCapture();
|
|
50
|
+
if (!HasUnsavedChanges) return;
|
|
51
|
+
string changed = draft.Keybinds.Any(b => { var previous = InterfacePreferences.Current.Keybinds.FirstOrDefault(p => p.Action == b.Action); return previous == null || previous.Key != b.Key || previous.Modifiers != b.Modifiers; }) ? "Some keybind or other settings haven't been saved." : "Some settings haven't been saved.";
|
|
52
|
+
if (!ConfirmLeave(delegate { return MessageBox.Show(this, changed + "\n\nWould you like to save these settings before exiting?", "Unsaved settings", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes; })) e.Cancel = true;
|
|
53
|
+
};
|
|
54
|
+
Closed += delegate { StopTest(); }; UpdateDirty();
|
|
55
|
+
}
|
|
56
|
+
private StackPanel Page(string title, string explanation)
|
|
57
|
+
{
|
|
58
|
+
var panel = new StackPanel { Margin = new Thickness(8, 0, 12, 8) };
|
|
59
|
+
panel.Children.Add(WorkspaceUi.Text(title, 22));
|
|
60
|
+
var hint = WorkspaceUi.Text(explanation, 14); hint.Margin = new Thickness(0, 9, 0, 18); panel.Children.Add(hint);
|
|
61
|
+
body.Content = new ScrollViewer { Content = panel, VerticalScrollBarVisibility = ScrollBarVisibility.Auto, HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled };
|
|
62
|
+
return panel;
|
|
63
|
+
}
|
|
64
|
+
private ComboBox Choice(Panel parent, string label, IEnumerable<string> values, string selected, Action<string> changed)
|
|
65
|
+
{
|
|
66
|
+
parent.Children.Add(WorkspaceUi.Text(label, 15));
|
|
67
|
+
var combo = new ComboBox { ItemsSource = values.ToArray(), SelectedItem = selected, MinHeight = 32, FontSize = 13, Margin = new Thickness(0, 7, 0, 18), Padding = new Thickness(6, 3, 6, 3) };
|
|
68
|
+
combo.SelectionChanged += delegate { if (combo.SelectedItem != null) { changed((string)combo.SelectedItem); UpdateDirty(); } }; parent.Children.Add(combo); return combo;
|
|
69
|
+
}
|
|
70
|
+
private void ShowCategory(string category)
|
|
71
|
+
{
|
|
72
|
+
if (category == "Timeline") { ShowTimeline(); return; }
|
|
73
|
+
if (category == "Keybinds") { ShowKeybinds(); return; }
|
|
74
|
+
if (category == "Audio") { ShowAudio(); return; }
|
|
75
|
+
if (category == "About") {
|
|
76
|
+
var about = Page("Flux", "A studio for recording, editing, listening and conversion.");
|
|
77
|
+
about.Children.Add(WorkspaceUi.Text("Basic editing and conversion work locally.\n\nWindows version " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(3) + "\n\nOriginal Drum Buddy mascot preserved in the source archive. Third-party licence notices are included with the app.", 14)); return;
|
|
78
|
+
}
|
|
79
|
+
var panel = Page("General", "Choose the appearance and startup screen. Changes take effect when saved.");
|
|
80
|
+
Choice(panel, "Theme", new[] { "Dark", "Light" }, draft.Theme, value => draft.Theme = value);
|
|
81
|
+
Choice(panel, "Open on startup", new[] { "Editor", "Home" }, draft.Startup, value => draft.Startup = value);
|
|
82
|
+
}
|
|
83
|
+
private void ShowTimeline()
|
|
84
|
+
{
|
|
85
|
+
var panel = Page("Timeline", "Set the current project's tempo grid and defaults for new sessions. Tempo changes the grid; audio keeps its original timing. Save applies changes.");
|
|
86
|
+
var form = new StackPanel { IsEnabled = !audioLocked }; panel.Children.Add(form);
|
|
87
|
+
var row = new WrapPanel(); form.Children.Add(row);
|
|
88
|
+
var tempo = new StackPanel { Width = 190, Margin = new Thickness(0, 0, 14, 15) }; row.Children.Add(tempo); tempo.Children.Add(WorkspaceUi.Text("Tempo (quarter-note BPM)", 14));
|
|
89
|
+
var tempoBox = new TextBox { Text = draft.Grid.Tempo.ToString("0.###", System.Globalization.CultureInfo.InvariantCulture), MinHeight = 32, Padding = new Thickness(6), Margin = new Thickness(0, 7, 0, 0) };
|
|
90
|
+
tempoBox.TextChanged += delegate { double value; draft.Grid.Tempo = TempoGrid.TryTempo(tempoBox.Text, out value) ? value : double.NaN; UpdateDirty(); }; tempo.Children.Add(tempoBox);
|
|
91
|
+
var beats = new StackPanel { Width = 150, Margin = new Thickness(0, 0, 14, 15) }; row.Children.Add(beats); beats.Children.Add(WorkspaceUi.Text("Beats per bar", 14));
|
|
92
|
+
var beatsBox = new TextBox { Text = draft.Grid.BeatsPerBar.ToString(), MinHeight = 32, Padding = new Thickness(6), Margin = new Thickness(0, 7, 0, 0) };
|
|
93
|
+
beatsBox.TextChanged += delegate { int value; draft.Grid.BeatsPerBar = int.TryParse(beatsBox.Text, out value) ? value : 0; UpdateDirty(); }; beats.Children.Add(beatsBox);
|
|
94
|
+
var unit = new StackPanel { Width = 160 }; row.Children.Add(unit);
|
|
95
|
+
Choice(unit, "Beat unit", new[] { "1/1", "1/2", "1/4", "1/8", "1/16", "1/32" }, "1/" + draft.Grid.BeatUnit, value => draft.Grid.BeatUnit = int.Parse(value.Substring(2)));
|
|
96
|
+
var choices = new WrapPanel(); form.Children.Add(choices);
|
|
97
|
+
var division = new StackPanel { Width = 190, Margin = new Thickness(0, 0, 14, 0) }; choices.Children.Add(division);
|
|
98
|
+
Choice(division, "Grid division", TempoGrid.Divisions.Select(v => "1/" + v), "1/" + draft.Grid.Division, value => draft.Grid.Division = int.Parse(value.Substring(2)));
|
|
99
|
+
var feel = new StackPanel { Width = 150, Margin = new Thickness(0, 0, 14, 0) }; choices.Children.Add(feel);
|
|
100
|
+
Choice(feel, "Subdivision", new[] { "Straight", "Triplet" }, draft.Grid.Triplet ? "Triplet" : "Straight", value => draft.Grid.Triplet = value == "Triplet");
|
|
101
|
+
var ruler = new StackPanel { Width = 160 }; choices.Children.Add(ruler);
|
|
102
|
+
Choice(ruler, "Ruler", new[] { "Bars / beats", "Seconds" }, draft.Grid.MusicalRuler ? "Bars / beats" : "Seconds", value => draft.Grid.MusicalRuler = value == "Bars / beats");
|
|
103
|
+
var snap = new CheckBox { Content = "Snap to grid", IsChecked = draft.SnapToGrid, Margin = new Thickness(0, 8, 0, 12) };
|
|
104
|
+
snap.Click += delegate { draft.SnapToGrid = snap.IsChecked == true; UpdateDirty(); }; form.Children.Add(snap);
|
|
105
|
+
var edges = new CheckBox { Content = "Snap to clip edges", IsChecked = draft.SnapToEdges, Margin = new Thickness(0, 0, 0, 18) };
|
|
106
|
+
edges.Click += delegate { draft.SnapToEdges = edges.IsChecked == true; UpdateDirty(); }; form.Children.Add(edges);
|
|
107
|
+
form.Children.Add(WorkspaceUi.Text("Alt bypasses snapping for sample-accurate cuts. Nearby clip edges take priority when edge snapping is on. Finer grid lines appear as you zoom in; snapping always uses the selected division.", 13));
|
|
108
|
+
if (audioLocked) panel.Children.Add(WorkspaceUi.Text("Finish the current recording or file operation to change timeline settings.", 13));
|
|
109
|
+
}
|
|
110
|
+
private void ShowAudio()
|
|
111
|
+
{
|
|
112
|
+
var panel = Page("Audio", "Choose output and recording input. Compatibility mode helps with Realtek or shared-audio API errors.");
|
|
113
|
+
panel.Children.Add(WorkspaceUi.Text("Speakers / headphones", 15));
|
|
114
|
+
var outputs = AudioOutputs.Choices();
|
|
115
|
+
var output = new ComboBox { ItemsSource = outputs, SelectedItem = outputs.FirstOrDefault(c => c.Key == draft.AudioOutputKey), MinHeight = 34, FontSize = 13, Margin = new Thickness(0, 7, 0, 14), IsEnabled = !audioLocked };
|
|
116
|
+
panel.Children.Add(output); output.SelectionChanged += delegate { var item = output.SelectedItem as OutputChoice; if (item != null) { draft.AudioOutputKey = item.Key; UpdateDirty(); } };
|
|
117
|
+
panel.Children.Add(WorkspaceUi.Text("Recording input", 15)); var inputs = AudioInputs.Choices();
|
|
118
|
+
var input = new ComboBox { ItemsSource = inputs, SelectedItem = inputs.FirstOrDefault(c => c.Key == draft.AudioInputKey), MinHeight = 34, FontSize = 13, Margin = new Thickness(0, 7, 0, 14), IsEnabled = !audioLocked };
|
|
119
|
+
panel.Children.Add(input); input.SelectionChanged += delegate { var item = input.SelectedItem as InputChoice; if (item != null) { draft.AudioInputKey = item.Key; UpdateDirty(); } };
|
|
120
|
+
Choice(panel, "Playback buffer (milliseconds)", new[] { "40", "60", "80", "120", "180", "250" }, draft.PlaybackLatency.ToString(), value => draft.PlaybackLatency = int.Parse(value)).IsEnabled = !audioLocked;
|
|
121
|
+
var note = WorkspaceUi.Text(audioLocked ? "Stop and save recording before changing audio devices." : "A smaller buffer lowers playback delay; increase it if sound breaks up. Windows microphones capture 16-bit PCM. Select H5studio ASIO for native 32-bit float capture.", 13); panel.Children.Add(note);
|
|
122
|
+
var actions = new WrapPanel { Margin = new Thickness(0, 14, 0, 0) }; panel.Children.Add(actions);
|
|
123
|
+
var test = WorkspaceUi.Button("Test speakers", delegate { Test(output.SelectedItem as OutputChoice); }); test.IsEnabled = !audioLocked; actions.Children.Add(test);
|
|
124
|
+
actions.Children.Add(WorkspaceUi.Button("Refresh devices", delegate { ShowAudio(); }));
|
|
125
|
+
actions.Children.Add(WorkspaceUi.Button("Save diagnostics...", delegate {
|
|
126
|
+
var dialog = new Microsoft.Win32.SaveFileDialog { FileName = "Flux-Audio-Diagnostics.txt", Filter = "Text report|*.txt" };
|
|
127
|
+
if (dialog.ShowDialog(this) == true) try { File.WriteAllText(dialog.FileName, AudioDiagnostics.Report(input.SelectedItem as InputChoice) + "Pending settings output: " + draft.AudioOutputKey, System.Text.Encoding.UTF8); status.Text = "Audio diagnostics saved."; } catch (Exception ex) { status.Text = ex.Message; }
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
private void ShowKeybinds()
|
|
131
|
+
{
|
|
132
|
+
var panel = Page("Keybinds", "Click Edit, then press the shortcut. The field focuses and selects itself immediately. Escape cancels capture.");
|
|
133
|
+
foreach (var binding in draft.Keybinds.Where(b => InterfacePreferences.Actions.ContainsKey(b.Action))) {
|
|
134
|
+
var row = new Grid { Margin = new Thickness(0, 0, 0, 8) };
|
|
135
|
+
row.ColumnDefinitions.Add(new ColumnDefinition()); row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(150) }); row.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); row.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
|
136
|
+
var label = WorkspaceUi.Text(InterfacePreferences.Actions[binding.Action], 14); row.Children.Add(label);
|
|
137
|
+
var box = new TextBox { Text = binding.Gesture, IsReadOnly = true, IsReadOnlyCaretVisible = true, MinHeight = 30, FontSize = 13, Padding = new Thickness(7, 4, 7, 4), Margin = new Thickness(8, 0, 8, 0) }; Grid.SetColumn(box, 1); row.Children.Add(box);
|
|
138
|
+
var edit = WorkspaceUi.Button("Edit", delegate { }); edit.Margin = new Thickness(0, 0, 6, 0); edit.Click += delegate { BeginCapture(binding, box, edit); }; Grid.SetColumn(edit, 2); row.Children.Add(edit);
|
|
139
|
+
var clear = WorkspaceUi.Button("Clear", delegate { CancelCapture(); binding.Key = "None"; binding.Modifiers = 0; box.Text = binding.Gesture; UpdateDirty(); }); clear.Margin = new Thickness(0); Grid.SetColumn(clear, 3); row.Children.Add(clear);
|
|
140
|
+
panel.Children.Add(row);
|
|
141
|
+
}
|
|
142
|
+
panel.Children.Add(WorkspaceUi.Button("Restore default keybinds", delegate { CancelCapture(); draft.Keybinds = InterfacePreferences.Defaults().Keybinds; ShowKeybinds(); UpdateDirty(); }));
|
|
143
|
+
}
|
|
144
|
+
private void BeginCapture(ShortcutBinding binding, TextBox box, Button edit)
|
|
145
|
+
{
|
|
146
|
+
CancelCapture(); capture = box; captureBinding = binding; captureButton = edit;
|
|
147
|
+
box.Text = "Press shortcut..."; edit.Content = "Listening"; box.Focus(); Keyboard.Focus(box); box.SelectAll();
|
|
148
|
+
Dispatcher.BeginInvoke((Action)delegate { if (capture == box) { box.Focus(); Keyboard.Focus(box); box.SelectAll(); } }, System.Windows.Threading.DispatcherPriority.Input);
|
|
149
|
+
}
|
|
150
|
+
private void CancelCapture()
|
|
151
|
+
{ if (capture == null) return; capture.Text = captureBinding.Gesture; captureButton.Content = "Edit"; capture = null; captureBinding = null; captureButton = null; }
|
|
152
|
+
private void OnKey(object sender, KeyEventArgs e)
|
|
153
|
+
{
|
|
154
|
+
Key key = e.Key == Key.System ? e.SystemKey : e.Key;
|
|
155
|
+
if (capture != null) {
|
|
156
|
+
e.Handled = true; if (key == Key.Escape) { CancelCapture(); return; }
|
|
157
|
+
if (e.IsRepeat || InterfacePreferences.IsModifier(key)) return;
|
|
158
|
+
AcceptCapture(key, Keyboard.Modifiers); return;
|
|
159
|
+
}
|
|
160
|
+
if (key == Key.S && Keyboard.Modifiers == ModifierKeys.Control) { Save(); e.Handled = true; }
|
|
161
|
+
else if (key == Key.Escape) { Close(); e.Handled = true; }
|
|
162
|
+
}
|
|
163
|
+
private void UpdateDirty() { save.IsEnabled = HasUnsavedChanges; Title = "Flux - Settings" + (HasUnsavedChanges ? " *" : ""); status.Text = HasUnsavedChanges ? "Unsaved settings" : "All settings saved"; }
|
|
164
|
+
private void Save() { CancelCapture(); SaveNow(); }
|
|
165
|
+
private bool SaveNow()
|
|
166
|
+
{
|
|
167
|
+
try { persist(draft); saved = draft.Signature(); ThemePalette.Install(this); UpdateDirty(); return true; }
|
|
168
|
+
catch (Exception ex) { status.Text = "Could not save: " + ex.Message; return false; }
|
|
169
|
+
}
|
|
170
|
+
private void Test(OutputChoice choice)
|
|
171
|
+
{
|
|
172
|
+
StopTest();
|
|
173
|
+
if (choice == null) { status.Text = "Choose a playback output first."; return; }
|
|
174
|
+
try {
|
|
175
|
+
testOutput = AudioOutputs.Open(new Signal(), choice); var current = testOutput;
|
|
176
|
+
testOutput.Player.PlaybackStopped += delegate(object sender, StoppedEventArgs e) { Dispatcher.BeginInvoke((Action)delegate { if (testOutput != current) return; StopTest(); status.Text = e.Exception == null ? "Speaker test finished" : e.Exception.Message; }); };
|
|
177
|
+
testOutput.Player.Play(); status.Text = "Playing a short test tone through the selected output";
|
|
178
|
+
} catch (Exception ex) { StopTest(); status.Text = ex.Message; }
|
|
179
|
+
}
|
|
180
|
+
private void StopTest() { var old = testOutput; testOutput = null; if (old != null) old.Dispose(); }
|
|
181
|
+
private void AcceptCapture(Key key, ModifierKeys modifiers)
|
|
182
|
+
{ if (captureBinding == null) return; captureBinding.Key = key.ToString(); captureBinding.Modifiers = (int)modifiers; CancelCapture(); UpdateDirty(); }
|
|
183
|
+
private bool ConfirmLeave(Func<bool> shouldSave)
|
|
184
|
+
{ return !HasUnsavedChanges || !shouldSave() || SaveNow(); }
|
|
185
|
+
private sealed class Signal : ISampleProvider
|
|
186
|
+
{
|
|
187
|
+
private int frame;
|
|
188
|
+
public WaveFormat WaveFormat { get { return WaveFormat.CreateIeeeFloatWaveFormat(44100, 2); } }
|
|
189
|
+
public int Read(float[] buffer, int offset, int count) {
|
|
190
|
+
int frames = Math.Min(count / 2, 15435 - frame);
|
|
191
|
+
for (int i = 0; i < frames; i++, frame++) { float value = (float)(.04 * Math.Min(1, Math.Min(frame, 15435 - frame) / 441.0) * Math.Sin(frame * 2 * Math.PI * 440 / 44100)); buffer[offset + i * 2] = buffer[offset + i * 2 + 1] = value; }
|
|
192
|
+
return frames * 2;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
internal void SetDraftForTest(InterfaceSettings value) { draft.Theme = value.Theme; draft.Startup = value.Startup; draft.AudioInputKey = value.AudioInputKey; draft.AudioOutputKey = value.AudioOutputKey; draft.PlaybackLatency = value.PlaybackLatency; draft.Grid = value.Grid.Copy(); draft.SnapToGrid = value.SnapToGrid; draft.SnapToEdges = value.SnapToEdges; draft.Keybinds = value.Keybinds.Select(b => b.Copy()).ToList(); UpdateDirty(); }
|
|
196
|
+
internal bool CaptureForTest(string action, Key key, ModifierKeys modifiers)
|
|
197
|
+
{
|
|
198
|
+
ShowKeybinds();
|
|
199
|
+
var panel = (StackPanel)((ScrollViewer)body.Content).Content;
|
|
200
|
+
var row = panel.Children.OfType<Grid>().First(r => ((TextBlock)r.Children[0]).Text == InterfacePreferences.Actions[action]);
|
|
201
|
+
var box = (TextBox)row.Children[1]; BeginCapture(draft.Keybinds.First(b => b.Action == action), box, (Button)row.Children[2]);
|
|
202
|
+
bool selected = capture == box && box.SelectionLength == box.Text.Length && box.Text == "Press shortcut...";
|
|
203
|
+
AcceptCapture(key, modifiers); return selected && capture == null && box.Text == draft.Keybinds.First(b => b.Action == action).Gesture;
|
|
204
|
+
}
|
|
205
|
+
internal bool LeaveForTest(bool save) { return ConfirmLeave(delegate { return save; }); }
|
|
206
|
+
internal InterfaceSettings DraftForTest { get { return draft.Copy(); } }
|
|
207
|
+
}
|
|
208
|
+
}
|
package/source/Setup.cs
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.IO;
|
|
3
|
+
using System.IO.Compression;
|
|
4
|
+
using System.Text;
|
|
5
|
+
using System.Linq;
|
|
6
|
+
using System.Reflection;
|
|
7
|
+
using System.Diagnostics;
|
|
8
|
+
using System.Threading.Tasks;
|
|
9
|
+
using System.Collections.Generic;
|
|
10
|
+
using System.Security.Cryptography;
|
|
11
|
+
using System.Windows;
|
|
12
|
+
using System.Windows.Controls;
|
|
13
|
+
using System.Windows.Markup;
|
|
14
|
+
using Microsoft.Win32;
|
|
15
|
+
|
|
16
|
+
[assembly: AssemblyTitle("Flux Setup")]
|
|
17
|
+
[assembly: AssemblyVersion("1.5.0.0")]
|
|
18
|
+
|
|
19
|
+
namespace DrumBuddySetup
|
|
20
|
+
{
|
|
21
|
+
public static class Setup
|
|
22
|
+
{
|
|
23
|
+
public static readonly string InstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"Programs","DrumBuddy");
|
|
24
|
+
public const string RegistryKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall\DrumBuddy";
|
|
25
|
+
private static Window window;
|
|
26
|
+
private static bool working, installed;
|
|
27
|
+
private static T C<T>(string name) where T : FrameworkElement { return (T)window.FindName(name); }
|
|
28
|
+
|
|
29
|
+
[STAThread]
|
|
30
|
+
public static int Main(string[] args)
|
|
31
|
+
{
|
|
32
|
+
// An extraction-only check permits validation without installing or changing Windows.
|
|
33
|
+
if (args.Length == 2 && args[0] == "--verify-payload") {
|
|
34
|
+
try { Extract(args[1],null); File.WriteAllText(Path.Combine(args[1],"payload-verified.txt"),"All bundled files passed SHA-256 verification."); return 0; }
|
|
35
|
+
catch (Exception ex) { File.WriteAllText(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),"setup-verification-error.txt"),ex.ToString()); return 1; }
|
|
36
|
+
}
|
|
37
|
+
// Applies the same verified app payload without starting the separate vendor driver wizard.
|
|
38
|
+
if (args.Length == 1 && args[0] == "--update-app") {
|
|
39
|
+
try { Install(true,null); return 0; }
|
|
40
|
+
catch (Exception ex) { File.WriteAllText(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),"setup-update-error.txt"),ex.ToString()); return 1; }
|
|
41
|
+
}
|
|
42
|
+
var app = new Application();
|
|
43
|
+
using (var s = Assembly.GetExecutingAssembly().GetManifestResourceStream("Setup.xaml")) window = (Window)XamlReader.Load(s);
|
|
44
|
+
C<Image>("SetupLogo").Source = DrumBuddy.BrandArtwork.Raster();
|
|
45
|
+
window.Height = Math.Min(window.Height,SystemParameters.WorkArea.Height - 30);
|
|
46
|
+
C<Button>("InstallButton").Click += async delegate {
|
|
47
|
+
if (installed) { LaunchApp(); window.Close(); return; }
|
|
48
|
+
if (working) return;
|
|
49
|
+
bool driver = C<CheckBox>("DriverCheck").IsChecked == true;
|
|
50
|
+
bool shortcut = C<CheckBox>("ShortcutCheck").IsChecked == true;
|
|
51
|
+
working = true; C<Button>("InstallButton").IsEnabled = false; C<CheckBox>("DriverCheck").IsEnabled = false; C<CheckBox>("ShortcutCheck").IsEnabled = false;
|
|
52
|
+
C<ProgressBar>("SetupProgress").Visibility = Visibility.Visible;
|
|
53
|
+
C<TextBlock>("SetupStatus").Text = "Installing Flux…";
|
|
54
|
+
try {
|
|
55
|
+
await Task.Run(() => Install(shortcut,delegate(double percent) { window.Dispatcher.BeginInvoke((Action)delegate { C<ProgressBar>("SetupProgress").Value = percent * 100; }); }));
|
|
56
|
+
installed = true;
|
|
57
|
+
if (driver) {
|
|
58
|
+
DrumBuddy.DriverSetup.ShowGuide(window);
|
|
59
|
+
C<TextBlock>("SetupStatus").Text = "Flux is installed. Install Drivers on its home screen links to Zoom's official driver and setup instructions.";
|
|
60
|
+
}
|
|
61
|
+
else C<TextBlock>("SetupStatus").Text = "All set. Your desktop shortcut is ready.";
|
|
62
|
+
C<Button>("InstallButton").Content = "Open Flux";
|
|
63
|
+
}
|
|
64
|
+
catch (Exception ex) {
|
|
65
|
+
C<TextBlock>("SetupStatus").Text = "Setup could not finish: " + ex.Message;
|
|
66
|
+
C<Button>("InstallButton").Content = "Try again";
|
|
67
|
+
C<CheckBox>("ShortcutCheck").IsEnabled = true; C<CheckBox>("DriverCheck").IsEnabled = true;
|
|
68
|
+
}
|
|
69
|
+
finally { working = false; C<Button>("InstallButton").IsEnabled = true; }
|
|
70
|
+
};
|
|
71
|
+
window.Closing += delegate(object sender, System.ComponentModel.CancelEventArgs e) { if (working) { e.Cancel = true; C<TextBlock>("SetupStatus").Text = "Please let setup finish. If the Zoom driver installer is open, complete or close it first."; } };
|
|
72
|
+
app.MainWindow = window; return app.Run(window);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public static bool ZoomInstalled()
|
|
76
|
+
{
|
|
77
|
+
foreach (RegistryView view in new[] { RegistryView.Registry64,RegistryView.Registry32 }) {
|
|
78
|
+
using (var root = Microsoft.Win32.RegistryKey.OpenBaseKey(RegistryHive.LocalMachine,view))
|
|
79
|
+
using (var asio = root.OpenSubKey(@"SOFTWARE\ASIO"))
|
|
80
|
+
if (asio != null && asio.GetSubKeyNames().Any(n => n.IndexOf("R16",StringComparison.OrdinalIgnoreCase) >= 0 || n.IndexOf("R24",StringComparison.OrdinalIgnoreCase) >= 0)) return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private static void Install(bool shortcut, Action<double> progress)
|
|
86
|
+
{
|
|
87
|
+
if (Process.GetProcessesByName("DrumBuddy").Length > 0) throw new IOException("Close Flux, then click Try again.");
|
|
88
|
+
string parent = Path.GetDirectoryName(InstallPath);
|
|
89
|
+
Directory.CreateDirectory(parent);
|
|
90
|
+
string stage = Path.Combine(parent,".DrumBuddy-setup-" + Guid.NewGuid().ToString("N"));
|
|
91
|
+
try {
|
|
92
|
+
Extract(stage,progress);
|
|
93
|
+
AssetBootstrap.Ensure(stage,progress);
|
|
94
|
+
Directory.CreateDirectory(InstallPath);
|
|
95
|
+
foreach (string source in Directory.GetFiles(stage,"*",SearchOption.AllDirectories)) {
|
|
96
|
+
string relative = source.Substring(stage.Length + 1);
|
|
97
|
+
string destination = Path.Combine(InstallPath,relative);
|
|
98
|
+
Directory.CreateDirectory(Path.GetDirectoryName(destination));
|
|
99
|
+
File.Copy(source,destination,true);
|
|
100
|
+
}
|
|
101
|
+
File.WriteAllText(Path.Combine(InstallPath,".drumbuddy-installed"),"Flux 1.5.0\n" + InstallPath,Encoding.UTF8);
|
|
102
|
+
string[] files = Directory.GetFiles(stage,"*",SearchOption.AllDirectories).Select(f => f.Substring(stage.Length + 1)).Concat(new[] { ".drumbuddy-installed","installed-files.txt" }).ToArray();
|
|
103
|
+
File.WriteAllLines(Path.Combine(InstallPath,"installed-files.txt"),files,Encoding.UTF8);
|
|
104
|
+
string exe = Path.Combine(InstallPath,"DrumBuddy.exe");
|
|
105
|
+
MakeShortcut(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu),"Programs","Flux.lnk"),exe);
|
|
106
|
+
if (shortcut) MakeShortcut(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),"Flux.lnk"),exe);
|
|
107
|
+
RemoveLegacyShortcut(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu),"Programs","FluxTempo.lnk"),exe);
|
|
108
|
+
RemoveLegacyShortcut(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),"FluxTempo.lnk"),exe);
|
|
109
|
+
RemoveLegacyShortcut(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu),"Programs","Drum Buddy.lnk"),exe);
|
|
110
|
+
RemoveLegacyShortcut(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),"Drum Buddy.lnk"),exe);
|
|
111
|
+
using (var key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(RegistryKey)) {
|
|
112
|
+
key.SetValue("DisplayName","Flux"); key.SetValue("DisplayVersion","1.5.0"); key.SetValue("Publisher","Flux");
|
|
113
|
+
key.SetValue("InstallLocation",InstallPath); key.SetValue("DisplayIcon",exe);
|
|
114
|
+
key.SetValue("UninstallString","\"" + Path.Combine(InstallPath,"Uninstall.exe") + "\"");
|
|
115
|
+
key.SetValue("NoModify",1); key.SetValue("NoRepair",1);
|
|
116
|
+
long size = Directory.GetFiles(InstallPath,"*",SearchOption.AllDirectories).Sum(f => new FileInfo(f).Length);
|
|
117
|
+
key.SetValue("EstimatedSize",(int)(size / 1024));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
finally { try { if (Directory.Exists(stage)) Directory.Delete(stage,true); } catch { } }
|
|
121
|
+
}
|
|
122
|
+
private static void Extract(string folder, Action<double> progress)
|
|
123
|
+
{
|
|
124
|
+
string root = Path.GetFullPath(folder).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
|
|
125
|
+
Directory.CreateDirectory(root);
|
|
126
|
+
var hashes = new Dictionary<string,string>(StringComparer.OrdinalIgnoreCase);
|
|
127
|
+
using (var text = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("payload.sha256"))) {
|
|
128
|
+
string line; while ((line = text.ReadLine()) != null) if (line.Length > 66) hashes.Add(line.Substring(66),line.Substring(0,64));
|
|
129
|
+
}
|
|
130
|
+
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("payload.zip"))
|
|
131
|
+
using (var zip = new ZipArchive(stream,ZipArchiveMode.Read)) {
|
|
132
|
+
int count = 0;
|
|
133
|
+
foreach (var entry in zip.Entries) {
|
|
134
|
+
if (string.IsNullOrEmpty(entry.Name)) continue;
|
|
135
|
+
string relative = entry.FullName.Replace('/',Path.DirectorySeparatorChar);
|
|
136
|
+
string target = Path.GetFullPath(Path.Combine(root,relative));
|
|
137
|
+
if (!target.StartsWith(root,StringComparison.OrdinalIgnoreCase) || relative.Contains(":")) throw new IOException("Invalid installer file path.");
|
|
138
|
+
string expected;
|
|
139
|
+
if (!hashes.TryGetValue(entry.FullName,out expected)) throw new IOException("An installer file is not in the integrity manifest.");
|
|
140
|
+
Directory.CreateDirectory(Path.GetDirectoryName(target));
|
|
141
|
+
using (var input = entry.Open()) using (var output = new FileStream(target,FileMode.Create,FileAccess.Write)) input.CopyTo(output);
|
|
142
|
+
using (var sha = SHA256.Create()) using (var file = File.OpenRead(target)) {
|
|
143
|
+
string actual = BitConverter.ToString(sha.ComputeHash(file)).Replace("-","");
|
|
144
|
+
if (!string.Equals(actual,expected,StringComparison.OrdinalIgnoreCase)) throw new IOException("An installer file failed its integrity check. Please obtain a fresh copy.");
|
|
145
|
+
}
|
|
146
|
+
hashes.Remove(entry.FullName);
|
|
147
|
+
if (progress != null) progress((double)++count / zip.Entries.Count);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (hashes.Count > 0) throw new IOException("The installer is incomplete.");
|
|
151
|
+
}
|
|
152
|
+
private static void RemoveLegacyShortcut(string path, string exe)
|
|
153
|
+
{
|
|
154
|
+
if (!File.Exists(path)) return;
|
|
155
|
+
object shell = Activator.CreateInstance(Type.GetTypeFromProgID("WScript.Shell"));
|
|
156
|
+
try {
|
|
157
|
+
object link = shell.GetType().InvokeMember("CreateShortcut",BindingFlags.InvokeMethod,null,shell,new object[] { path });
|
|
158
|
+
try {
|
|
159
|
+
string target = (string)link.GetType().InvokeMember("TargetPath",BindingFlags.GetProperty,null,link,null);
|
|
160
|
+
if (string.Equals(target,exe,StringComparison.OrdinalIgnoreCase)) File.Delete(path);
|
|
161
|
+
}
|
|
162
|
+
finally { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(link); }
|
|
163
|
+
}
|
|
164
|
+
finally { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(shell); }
|
|
165
|
+
}
|
|
166
|
+
private static void MakeShortcut(string path, string exe)
|
|
167
|
+
{
|
|
168
|
+
object shell = Activator.CreateInstance(Type.GetTypeFromProgID("WScript.Shell"));
|
|
169
|
+
try {
|
|
170
|
+
object link = shell.GetType().InvokeMember("CreateShortcut",BindingFlags.InvokeMethod,null,shell,new object[] { path });
|
|
171
|
+
try {
|
|
172
|
+
Type type = link.GetType();
|
|
173
|
+
if (File.Exists(path)) {
|
|
174
|
+
string existing = (string)type.InvokeMember("TargetPath",BindingFlags.GetProperty,null,link,null);
|
|
175
|
+
if (!string.Equals(existing,exe,StringComparison.OrdinalIgnoreCase)) {
|
|
176
|
+
string alternate = Path.Combine(Path.GetDirectoryName(path),"Flux Audio.lnk");
|
|
177
|
+
if (string.Equals(path,alternate,StringComparison.OrdinalIgnoreCase)) throw new IOException("An existing Flux Audio shortcut belongs to another program.");
|
|
178
|
+
MakeShortcut(alternate,exe); return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
type.InvokeMember("TargetPath",BindingFlags.SetProperty,null,link,new object[] { exe });
|
|
182
|
+
type.InvokeMember("WorkingDirectory",BindingFlags.SetProperty,null,link,new object[] { Path.GetDirectoryName(exe) });
|
|
183
|
+
type.InvokeMember("IconLocation",BindingFlags.SetProperty,null,link,new object[] { exe + ",0" });
|
|
184
|
+
type.InvokeMember("Save",BindingFlags.InvokeMethod,null,link,null);
|
|
185
|
+
}
|
|
186
|
+
finally { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(link); }
|
|
187
|
+
}
|
|
188
|
+
finally { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(shell); }
|
|
189
|
+
}
|
|
190
|
+
private static void LaunchApp() { try { Process.Start(new ProcessStartInfo(Path.Combine(InstallPath,"DrumBuddy.exe")) { UseShellExecute = true }); } catch { } }
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
2
|
+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
3
|
+
Title="Install Flux" Width="650" Height="540" ResizeMode="NoResize"
|
|
4
|
+
WindowStartupLocation="CenterScreen" Background="#F3F5F5" FontFamily="Segoe UI" FontSize="18" Foreground="#182C37">
|
|
5
|
+
<Grid Margin="35">
|
|
6
|
+
<Grid.RowDefinitions><RowDefinition/><RowDefinition Height="Auto"/></Grid.RowDefinitions>
|
|
7
|
+
<StackPanel>
|
|
8
|
+
<StackPanel Orientation="Horizontal" Margin="0,0,0,20">
|
|
9
|
+
<Image x:Name="SetupLogo" Width="72" Height="87" Stretch="Uniform" Margin="0,0,18,0"/>
|
|
10
|
+
<StackPanel VerticalAlignment="Center">
|
|
11
|
+
<TextBlock Text="Flux" FontSize="36" FontWeight="Bold"/>
|
|
12
|
+
<TextBlock Text="Ready for your next drum session." Foreground="#526771" FontSize="18" Margin="0,5,0,0"/>
|
|
13
|
+
</StackPanel>
|
|
14
|
+
</StackPanel>
|
|
15
|
+
<Border Background="White" CornerRadius="12" Padding="23" BorderBrush="#DFE5E6" BorderThickness="1">
|
|
16
|
+
<StackPanel>
|
|
17
|
+
<TextBlock Text="Play recordings. Edit up to 16 tracks. Save WAVs." TextWrapping="Wrap" FontSize="19" FontWeight="SemiBold"/>
|
|
18
|
+
<TextBlock Text="Record H5studio float audio or your Zoom R16." Margin="0,9,0,17" FontSize="18" TextWrapping="Wrap"/>
|
|
19
|
+
<CheckBox x:Name="DriverCheck" Content="Show the official Zoom driver setup guide" IsChecked="False" FontSize="18" Margin="0,0,0,12"/>
|
|
20
|
+
<CheckBox x:Name="ShortcutCheck" Content="Add a shortcut to my desktop" IsChecked="True" FontSize="18"/>
|
|
21
|
+
</StackPanel>
|
|
22
|
+
</Border>
|
|
23
|
+
<TextBlock x:Name="SetupStatus" Text="Setup prepares the audio tools. Install Drivers links to Zoom's official H5studio and R16 downloads. Your audio files are kept when updating." TextWrapping="Wrap" FontSize="16" Foreground="#526771" Margin="0,17,0,8"/>
|
|
24
|
+
<ProgressBar x:Name="SetupProgress" Height="8" Minimum="0" Maximum="100" Visibility="Hidden"/>
|
|
25
|
+
</StackPanel>
|
|
26
|
+
<Button x:Name="InstallButton" Grid.Row="1" Content="Install Flux" FontSize="23" FontWeight="SemiBold" Height="65" Background="#006D68" Foreground="White" BorderThickness="0" Cursor="Hand"/>
|
|
27
|
+
</Grid>
|
|
28
|
+
</Window>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
|
2
|
+
<Style TargetType="ListBox"><Setter Property="Background" Value="{DynamicResource ThemeFFFFFF}"/><Setter Property="Foreground" Value="{DynamicResource Theme182C37}"/><Setter Property="BorderBrush" Value="{DynamicResource ThemeC3CED0}"/></Style>
|
|
3
|
+
<Style TargetType="ProgressBar"><Setter Property="Background" Value="{DynamicResource ThemeE6ECEC}"/><Setter Property="Foreground" Value="{DynamicResource Theme006D68}"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="ProgressBar"><Grid x:Name="PART_Track"><Border Background="{TemplateBinding Background}" CornerRadius="3"/><Border x:Name="PART_Indicator" HorizontalAlignment="Left" Background="{TemplateBinding Foreground}" CornerRadius="3"/></Grid></ControlTemplate></Setter.Value></Setter></Style>
|
|
4
|
+
<Style TargetType="Button">
|
|
5
|
+
<Setter Property="Background" Value="{DynamicResource ThemeFFFFFF}"/><Setter Property="Foreground" Value="{DynamicResource Theme182C37}"/><Setter Property="BorderBrush" Value="{DynamicResource ThemeC3CED0}"/>
|
|
6
|
+
<Setter Property="Padding" Value="10,5"/><Setter Property="MinHeight" Value="30"/><Setter Property="FontSize" Value="13"/><Setter Property="Cursor" Value="Hand"/>
|
|
7
|
+
<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="Button"><Border x:Name="Frame" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1" CornerRadius="5" Padding="{TemplateBinding Padding}"><ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/></Border><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter TargetName="Frame" Property="BorderBrush" Value="{DynamicResource Theme006D68}"/></Trigger><Trigger Property="IsKeyboardFocused" Value="True"><Setter TargetName="Frame" Property="BorderBrush" Value="{DynamicResource Theme006D68}"/></Trigger><Trigger Property="IsPressed" Value="True"><Setter Property="Opacity" Value="0.7"/></Trigger><Trigger Property="IsEnabled" Value="False"><Setter Property="Opacity" Value="0.4"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter>
|
|
8
|
+
</Style>
|
|
9
|
+
<Style TargetType="ToggleButton">
|
|
10
|
+
<Setter Property="Background" Value="{DynamicResource ThemeFFFFFF}"/><Setter Property="Foreground" Value="{DynamicResource Theme182C37}"/><Setter Property="BorderBrush" Value="{DynamicResource ThemeC3CED0}"/>
|
|
11
|
+
<Setter Property="Padding" Value="7,4"/><Setter Property="FontSize" Value="12"/>
|
|
12
|
+
<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="ToggleButton"><Border x:Name="Frame" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1" CornerRadius="4" Padding="{TemplateBinding Padding}"><ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/></Border><ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter TargetName="Frame" Property="Background" Value="{DynamicResource Theme006D68}"/><Setter Property="Foreground" Value="{DynamicResource ThemeOnAccent}"/></Trigger><Trigger Property="IsEnabled" Value="False"><Setter Property="Opacity" Value="0.4"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter>
|
|
13
|
+
</Style>
|
|
14
|
+
<Style TargetType="ListBoxItem">
|
|
15
|
+
<Setter Property="Foreground" Value="{DynamicResource Theme182C37}"/><Setter Property="Padding" Value="8,7"/><Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
|
16
|
+
<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="ListBoxItem"><Border x:Name="Row" Background="Transparent" CornerRadius="4" Padding="{TemplateBinding Padding}"><ContentPresenter/></Border><ControlTemplate.Triggers><Trigger Property="IsSelected" Value="True"><Setter TargetName="Row" Property="Background" Value="{DynamicResource ThemeE6ECEC}"/></Trigger><Trigger Property="IsMouseOver" Value="True"><Setter TargetName="Row" Property="Background" Value="{DynamicResource ThemeE6ECEC}"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter>
|
|
17
|
+
</Style>
|
|
18
|
+
<Style TargetType="CheckBox">
|
|
19
|
+
<Setter Property="Foreground" Value="{DynamicResource Theme182C37}"/><Setter Property="FontSize" Value="13"/>
|
|
20
|
+
<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="CheckBox"><DockPanel LastChildFill="True"><Border x:Name="Box" DockPanel.Dock="Left" Width="14" Height="14" CornerRadius="3" BorderThickness="1" BorderBrush="{DynamicResource ThemeC3CED0}" Background="{DynamicResource ThemeFFFFFF}" VerticalAlignment="Center"><Path x:Name="Check" Data="M 2,6 L 5,9 L 10,3" Stroke="{DynamicResource ThemeOnAccent}" StrokeThickness="1.8" Visibility="Collapsed"/></Border><ContentPresenter ContentSource="Content" Margin="7,0,0,0" VerticalAlignment="Center" RecognizesAccessKey="True"/></DockPanel><ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter TargetName="Box" Property="Background" Value="{DynamicResource Theme006D68}"/><Setter TargetName="Check" Property="Visibility" Value="Visible"/></Trigger><Trigger Property="IsEnabled" Value="False"><Setter Property="Opacity" Value="0.4"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter>
|
|
21
|
+
</Style>
|
|
22
|
+
<Style TargetType="RadioButton">
|
|
23
|
+
<Setter Property="Foreground" Value="{DynamicResource Theme182C37}"/><Setter Property="FontSize" Value="13"/><Setter Property="Margin" Value="0,4,12,4"/>
|
|
24
|
+
<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="RadioButton"><DockPanel LastChildFill="True"><Grid DockPanel.Dock="Left" Width="14" Height="14" VerticalAlignment="Center"><Ellipse Stroke="{DynamicResource ThemeC3CED0}" Fill="{DynamicResource ThemeFFFFFF}"/><Ellipse x:Name="Dot" Width="7" Height="7" Fill="{DynamicResource Theme006D68}" Visibility="Collapsed"/></Grid><ContentPresenter ContentSource="Content" Margin="6,0,0,0" VerticalAlignment="Center" RecognizesAccessKey="True"/></DockPanel><ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter TargetName="Dot" Property="Visibility" Value="Visible"/></Trigger><Trigger Property="IsEnabled" Value="False"><Setter Property="Opacity" Value="0.4"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter>
|
|
25
|
+
</Style>
|
|
26
|
+
<Style TargetType="Slider">
|
|
27
|
+
<Setter Property="Height" Value="22"/>
|
|
28
|
+
<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="Slider"><Track x:Name="PART_Track" Minimum="{TemplateBinding Minimum}" Maximum="{TemplateBinding Maximum}" Value="{Binding Value,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" VerticalAlignment="Center">
|
|
29
|
+
<Track.DecreaseRepeatButton><RepeatButton Command="Slider.DecreaseLarge"><RepeatButton.Template><ControlTemplate TargetType="RepeatButton"><Border Background="{DynamicResource Theme006D68}" Height="3" CornerRadius="1"/></ControlTemplate></RepeatButton.Template></RepeatButton></Track.DecreaseRepeatButton>
|
|
30
|
+
<Track.Thumb><Thumb Width="12" Height="12"><Thumb.Template><ControlTemplate TargetType="Thumb"><Ellipse Fill="{DynamicResource Theme182C37}"/></ControlTemplate></Thumb.Template></Thumb></Track.Thumb>
|
|
31
|
+
<Track.IncreaseRepeatButton><RepeatButton Command="Slider.IncreaseLarge"><RepeatButton.Template><ControlTemplate TargetType="RepeatButton"><Border Background="{DynamicResource ThemeC3CED0}" Height="3" CornerRadius="1"/></ControlTemplate></RepeatButton.Template></RepeatButton></Track.IncreaseRepeatButton>
|
|
32
|
+
</Track></ControlTemplate></Setter.Value></Setter>
|
|
33
|
+
</Style>
|
|
34
|
+
<Style TargetType="TextBox">
|
|
35
|
+
<Setter Property="Background" Value="{DynamicResource ThemeFFFFFF}"/><Setter Property="Foreground" Value="{DynamicResource Theme182C37}"/>
|
|
36
|
+
<Setter Property="BorderBrush" Value="{DynamicResource ThemeC3CED0}"/><Setter Property="CaretBrush" Value="{DynamicResource Theme182C37}"/>
|
|
37
|
+
<Setter Property="SelectionBrush" Value="{DynamicResource Theme006D68}"/><Setter Property="SelectionOpacity" Value="0.45"/>
|
|
38
|
+
<Setter Property="FontSize" Value="13"/><Setter Property="Padding" Value="6,4"/><Setter Property="MinHeight" Value="30"/>
|
|
39
|
+
</Style>
|
|
40
|
+
<Style TargetType="ComboBoxItem">
|
|
41
|
+
<Setter Property="Foreground" Value="{DynamicResource Theme182C37}"/><Setter Property="Background" Value="Transparent"/>
|
|
42
|
+
<Setter Property="Padding" Value="8,6"/><Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
|
43
|
+
<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="ComboBoxItem">
|
|
44
|
+
<Border x:Name="Row" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"><ContentPresenter/></Border>
|
|
45
|
+
<ControlTemplate.Triggers><Trigger Property="IsHighlighted" Value="True"><Setter TargetName="Row" Property="Background" Value="{DynamicResource ThemeE6ECEC}"/></Trigger>
|
|
46
|
+
<Trigger Property="IsSelected" Value="True"><Setter TargetName="Row" Property="Background" Value="{DynamicResource ThemeE6ECEC}"/></Trigger>
|
|
47
|
+
</ControlTemplate.Triggers>
|
|
48
|
+
</ControlTemplate></Setter.Value></Setter>
|
|
49
|
+
</Style>
|
|
50
|
+
<Style TargetType="ComboBox">
|
|
51
|
+
<Setter Property="FontSize" Value="13"/><Setter Property="MinHeight" Value="30"/><Setter Property="Padding" Value="7,4"/>
|
|
52
|
+
<Setter Property="Background" Value="{DynamicResource ThemeFFFFFF}"/><Setter Property="Foreground" Value="{DynamicResource Theme182C37}"/><Setter Property="BorderBrush" Value="{DynamicResource ThemeC3CED0}"/>
|
|
53
|
+
<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="ComboBox">
|
|
54
|
+
<Grid>
|
|
55
|
+
<ToggleButton Focusable="False" ClickMode="Press" IsChecked="{Binding IsDropDownOpen,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}">
|
|
56
|
+
<ToggleButton.Template><ControlTemplate TargetType="ToggleButton"><Border Background="{Binding Background,RelativeSource={RelativeSource AncestorType=ComboBox}}" BorderBrush="{Binding BorderBrush,RelativeSource={RelativeSource AncestorType=ComboBox}}" BorderThickness="1" CornerRadius="5"><TextBlock Text="⌄" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,8,3" Foreground="{DynamicResource Theme526771}"/></Border></ControlTemplate></ToggleButton.Template>
|
|
57
|
+
</ToggleButton>
|
|
58
|
+
<ContentPresenter Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Margin="8,4,27,4" VerticalAlignment="Center" HorizontalAlignment="Left" IsHitTestVisible="False"/>
|
|
59
|
+
<Popup x:Name="PART_Popup" IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom" AllowsTransparency="True" Focusable="False">
|
|
60
|
+
<Border Background="{DynamicResource ThemeFFFFFF}" BorderBrush="{DynamicResource ThemeC3CED0}" BorderThickness="1" CornerRadius="5" MinWidth="{Binding ActualWidth,RelativeSource={RelativeSource TemplatedParent}}" MaxHeight="350" Padding="3"><ScrollViewer CanContentScroll="True" VerticalScrollBarVisibility="Auto"><ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained"/></ScrollViewer></Border>
|
|
61
|
+
</Popup>
|
|
62
|
+
</Grid>
|
|
63
|
+
<ControlTemplate.Triggers><Trigger Property="IsEnabled" Value="False"><Setter Property="Opacity" Value="0.45"/></Trigger></ControlTemplate.Triggers>
|
|
64
|
+
</ControlTemplate></Setter.Value></Setter>
|
|
65
|
+
</Style>
|
|
66
|
+
<Style TargetType="MenuItem">
|
|
67
|
+
<Setter Property="Foreground" Value="{DynamicResource Theme182C37}"/><Setter Property="Background" Value="Transparent"/><Setter Property="Padding" Value="10,6"/><Setter Property="FontSize" Value="13"/>
|
|
68
|
+
<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="MenuItem">
|
|
69
|
+
<Border x:Name="Row" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" CornerRadius="4">
|
|
70
|
+
<Grid><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition Width="Auto"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions>
|
|
71
|
+
<ContentPresenter ContentSource="Header" RecognizesAccessKey="True" VerticalAlignment="Center"/>
|
|
72
|
+
<TextBlock x:Name="Gesture" Grid.Column="1" Text="{TemplateBinding InputGestureText}" Foreground="{DynamicResource Theme526771}" Margin="24,0,4,0" VerticalAlignment="Center"/>
|
|
73
|
+
<TextBlock x:Name="More" Grid.Column="2" Text="›" Margin="10,0,0,0" Visibility="Collapsed"/>
|
|
74
|
+
<Popup x:Name="PART_Popup" IsOpen="{TemplateBinding IsSubmenuOpen}" Placement="Right" AllowsTransparency="True" Focusable="False">
|
|
75
|
+
<Border Background="{DynamicResource ThemeFFFFFF}" BorderBrush="{DynamicResource ThemeC3CED0}" BorderThickness="1" CornerRadius="6" Padding="4" MinWidth="220"><StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/></Border>
|
|
76
|
+
</Popup>
|
|
77
|
+
</Grid>
|
|
78
|
+
</Border>
|
|
79
|
+
<ControlTemplate.Triggers>
|
|
80
|
+
<Trigger Property="Role" Value="TopLevelHeader"><Setter TargetName="PART_Popup" Property="Placement" Value="Bottom"/></Trigger>
|
|
81
|
+
<Trigger Property="HasItems" Value="True"><Setter TargetName="More" Property="Visibility" Value="Visible"/></Trigger>
|
|
82
|
+
<Trigger Property="Role" Value="TopLevelHeader"><Setter TargetName="More" Property="Visibility" Value="Collapsed"/></Trigger>
|
|
83
|
+
<Trigger Property="Role" Value="TopLevelHeader"><Setter TargetName="Gesture" Property="Visibility" Value="Collapsed"/></Trigger>
|
|
84
|
+
<Trigger Property="IsHighlighted" Value="True"><Setter TargetName="Row" Property="Background" Value="{DynamicResource ThemeE6ECEC}"/></Trigger>
|
|
85
|
+
<Trigger Property="IsSubmenuOpen" Value="True"><Setter TargetName="Row" Property="Background" Value="{DynamicResource ThemeE6ECEC}"/></Trigger>
|
|
86
|
+
<Trigger Property="IsEnabled" Value="False"><Setter Property="Opacity" Value="0.45"/></Trigger>
|
|
87
|
+
</ControlTemplate.Triggers>
|
|
88
|
+
</ControlTemplate></Setter.Value></Setter>
|
|
89
|
+
</Style>
|
|
90
|
+
<Style TargetType="ScrollBar">
|
|
91
|
+
<Setter Property="Width" Value="10"/><Setter Property="Background" Value="Transparent"/>
|
|
92
|
+
<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="ScrollBar"><Track x:Name="PART_Track" Orientation="{TemplateBinding Orientation}" IsDirectionReversed="True">
|
|
93
|
+
<Track.DecreaseRepeatButton><RepeatButton Command="ScrollBar.PageUpCommand" Opacity="0" Focusable="False"/></Track.DecreaseRepeatButton>
|
|
94
|
+
<Track.Thumb><Thumb><Thumb.Template><ControlTemplate TargetType="Thumb"><Border Background="{DynamicResource ThemeC3CED0}" CornerRadius="4" Margin="2"/></ControlTemplate></Thumb.Template></Thumb></Track.Thumb>
|
|
95
|
+
<Track.IncreaseRepeatButton><RepeatButton Command="ScrollBar.PageDownCommand" Opacity="0" Focusable="False"/></Track.IncreaseRepeatButton>
|
|
96
|
+
</Track><ControlTemplate.Triggers><Trigger Property="Orientation" Value="Horizontal"><Setter TargetName="PART_Track" Property="IsDirectionReversed" Value="False"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter>
|
|
97
|
+
<Style.Triggers><Trigger Property="Orientation" Value="Horizontal"><Setter Property="Width" Value="Auto"/><Setter Property="Height" Value="10"/></Trigger></Style.Triggers>
|
|
98
|
+
</Style>
|
|
99
|
+
</ResourceDictionary>
|