phosphor_app/state/
loop_editor.rs1use phosphor_core::transport::Transport;
8
9const TICKS_PER_BAR: i64 = Transport::PPQ * 4;
11
12#[derive(Debug)]
13pub struct LoopEditor {
14 pub active: bool,
16 pub enabled: bool,
18 pub start_bar: u32,
20 pub end_bar: u32,
22}
23
24impl Default for LoopEditor {
25 fn default() -> Self { Self::new() }
26}
27
28impl LoopEditor {
29 pub fn new() -> Self {
30 Self {
31 active: false,
32 enabled: false,
33 start_bar: 1,
34 end_bar: 5,
35 }
36 }
37
38 pub fn focus(&mut self) {
40 self.active = true;
41 }
42
43 pub fn unfocus(&mut self) {
45 self.active = false;
46 }
47
48 pub fn toggle_enabled(&mut self) {
50 self.enabled = !self.enabled;
51 }
52
53 pub fn move_start_left(&mut self) {
55 if self.start_bar > 1 {
56 self.start_bar -= 1;
57 }
58 }
59
60 pub fn move_start_right(&mut self) {
63 if self.start_bar + 1 < self.end_bar {
64 self.start_bar += 1;
65 }
66 }
67
68 pub fn move_end_left(&mut self) {
71 if self.end_bar > self.start_bar + 1 {
72 self.end_bar -= 1;
73 }
74 }
75
76 pub fn move_end_right(&mut self) {
78 self.end_bar += 1;
79 }
80
81 pub fn start_ticks(&self) -> i64 {
83 (self.start_bar as i64 - 1) * TICKS_PER_BAR
84 }
85
86 pub fn end_ticks(&self) -> i64 {
89 (self.end_bar as i64 - 1) * TICKS_PER_BAR
90 }
91
92 pub fn bar_count(&self) -> u32 {
94 self.end_bar - self.start_bar
95 }
96
97 pub fn display(&self) -> String {
99 format!("{}-{}", self.start_bar, self.end_bar - 1)
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn default_is_4_bars() {
109 let le = LoopEditor::new();
110 assert_eq!(le.start_bar, 1);
111 assert_eq!(le.end_bar, 5);
112 assert_eq!(le.bar_count(), 4);
113 assert_eq!(le.display(), "1-4");
114 }
115
116 #[test]
117 fn start_cant_go_below_1() {
118 let mut le = LoopEditor::new();
119 le.move_start_left();
120 le.move_start_left();
121 le.move_start_left();
122 assert_eq!(le.start_bar, 1);
123 }
124
125 #[test]
126 fn start_cant_pass_end() {
127 let mut le = LoopEditor::new();
128 for _ in 0..10 {
130 le.move_start_right();
131 }
132 assert_eq!(le.start_bar, 4);
133 assert!(le.start_bar < le.end_bar);
134 }
135
136 #[test]
137 fn end_cant_pass_start() {
138 let mut le = LoopEditor::new();
139 for _ in 0..10 {
141 le.move_end_left();
142 }
143 assert_eq!(le.end_bar, 2);
144 assert!(le.end_bar > le.start_bar);
145 }
146
147 #[test]
148 fn end_can_grow() {
149 let mut le = LoopEditor::new();
150 le.move_end_right();
151 le.move_end_right();
152 assert_eq!(le.end_bar, 7);
153 assert_eq!(le.display(), "1-6");
154 }
155
156 #[test]
157 fn ticks_correct() {
158 let le = LoopEditor::new();
159 assert_eq!(le.start_ticks(), 0);
161 assert_eq!(le.end_ticks(), 4 * TICKS_PER_BAR); }
163
164 #[test]
165 fn ticks_for_two_bars() {
166 let mut le = LoopEditor::new();
167 le.end_bar = 3;
169 assert_eq!(le.display(), "1-2");
170 assert_eq!(le.start_ticks(), 0);
171 assert_eq!(le.end_ticks(), 2 * TICKS_PER_BAR); }
173
174 #[test]
175 fn focus_unfocus() {
176 let mut le = LoopEditor::new();
177 assert!(!le.active);
178 le.focus();
179 assert!(le.active);
180 le.unfocus();
181 assert!(!le.active);
182 }
183
184 #[test]
185 fn enabled_toggle() {
186 let mut le = LoopEditor::new();
187 assert!(!le.enabled);
188 le.toggle_enabled();
189 assert!(le.enabled);
190 le.toggle_enabled();
191 assert!(!le.enabled);
192 }
193
194 #[test]
195 fn focus_does_not_enable() {
196 let mut le = LoopEditor::new();
197 le.focus();
198 assert!(le.active);
199 assert!(!le.enabled);
200 }
201}