243 lines
6.6 KiB
Rust
243 lines
6.6 KiB
Rust
use super::CyclicEnum;
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
pub enum OptionsFocus {
|
|
#[default]
|
|
ColorScheme,
|
|
HueRotation,
|
|
RefreshRate,
|
|
RuntimeHighlight,
|
|
ShowScope,
|
|
ShowSpectrum,
|
|
ShowLissajous,
|
|
GainBoost,
|
|
NormalizeViz,
|
|
ShowCompletion,
|
|
ShowPreview,
|
|
PerformanceMode,
|
|
Font,
|
|
ZoomFactor,
|
|
WindowSize,
|
|
LinkEnabled,
|
|
StartStopSync,
|
|
Quantum,
|
|
MidiOutput0,
|
|
MidiOutput1,
|
|
MidiOutput2,
|
|
MidiOutput3,
|
|
MidiInput0,
|
|
MidiInput1,
|
|
MidiInput2,
|
|
MidiInput3,
|
|
ResetOnboarding,
|
|
LoadDemoOnStartup,
|
|
}
|
|
|
|
impl CyclicEnum for OptionsFocus {
|
|
const VARIANTS: &'static [Self] = &[
|
|
Self::ColorScheme,
|
|
Self::HueRotation,
|
|
Self::RefreshRate,
|
|
Self::RuntimeHighlight,
|
|
Self::ShowScope,
|
|
Self::ShowSpectrum,
|
|
Self::ShowLissajous,
|
|
Self::GainBoost,
|
|
Self::NormalizeViz,
|
|
Self::ShowCompletion,
|
|
Self::ShowPreview,
|
|
Self::PerformanceMode,
|
|
Self::Font,
|
|
Self::ZoomFactor,
|
|
Self::WindowSize,
|
|
Self::LinkEnabled,
|
|
Self::StartStopSync,
|
|
Self::Quantum,
|
|
Self::MidiOutput0,
|
|
Self::MidiOutput1,
|
|
Self::MidiOutput2,
|
|
Self::MidiOutput3,
|
|
Self::MidiInput0,
|
|
Self::MidiInput1,
|
|
Self::MidiInput2,
|
|
Self::MidiInput3,
|
|
Self::ResetOnboarding,
|
|
Self::LoadDemoOnStartup,
|
|
];
|
|
}
|
|
|
|
const PLUGIN_ONLY: &[OptionsFocus] = &[
|
|
OptionsFocus::Font,
|
|
OptionsFocus::ZoomFactor,
|
|
OptionsFocus::WindowSize,
|
|
];
|
|
|
|
const STANDALONE_ONLY: &[OptionsFocus] = &[
|
|
OptionsFocus::LinkEnabled,
|
|
OptionsFocus::StartStopSync,
|
|
OptionsFocus::Quantum,
|
|
OptionsFocus::MidiOutput0,
|
|
OptionsFocus::MidiOutput1,
|
|
OptionsFocus::MidiOutput2,
|
|
OptionsFocus::MidiOutput3,
|
|
OptionsFocus::MidiInput0,
|
|
OptionsFocus::MidiInput1,
|
|
OptionsFocus::MidiInput2,
|
|
OptionsFocus::MidiInput3,
|
|
OptionsFocus::ResetOnboarding,
|
|
OptionsFocus::LoadDemoOnStartup,
|
|
];
|
|
|
|
/// Section layout: header line, divider line, then option lines.
|
|
/// Each entry gives the raw line offsets assuming ALL sections are visible
|
|
/// (plugin mode with Font/Zoom/Window shown).
|
|
const FULL_LAYOUT: &[(OptionsFocus, usize)] = &[
|
|
// DISPLAY section: header=0, divider=1
|
|
(OptionsFocus::ColorScheme, 2),
|
|
(OptionsFocus::HueRotation, 3),
|
|
(OptionsFocus::RefreshRate, 4),
|
|
(OptionsFocus::RuntimeHighlight, 5),
|
|
(OptionsFocus::ShowScope, 6),
|
|
(OptionsFocus::ShowSpectrum, 7),
|
|
(OptionsFocus::ShowLissajous, 8),
|
|
(OptionsFocus::GainBoost, 9),
|
|
(OptionsFocus::NormalizeViz, 10),
|
|
(OptionsFocus::ShowCompletion, 11),
|
|
(OptionsFocus::ShowPreview, 12),
|
|
(OptionsFocus::PerformanceMode, 13),
|
|
(OptionsFocus::Font, 14),
|
|
(OptionsFocus::ZoomFactor, 15),
|
|
(OptionsFocus::WindowSize, 16),
|
|
// blank=17, ABLETON LINK header=18, divider=19
|
|
(OptionsFocus::LinkEnabled, 20),
|
|
(OptionsFocus::StartStopSync, 21),
|
|
(OptionsFocus::Quantum, 22),
|
|
// blank=23, SESSION header=24, divider=25, Tempo=26, Beat=27, Phase=28
|
|
// blank=29, MIDI OUTPUTS header=30, divider=31
|
|
(OptionsFocus::MidiOutput0, 32),
|
|
(OptionsFocus::MidiOutput1, 33),
|
|
(OptionsFocus::MidiOutput2, 34),
|
|
(OptionsFocus::MidiOutput3, 35),
|
|
// blank=36, MIDI INPUTS header=37, divider=38
|
|
(OptionsFocus::MidiInput0, 39),
|
|
(OptionsFocus::MidiInput1, 40),
|
|
(OptionsFocus::MidiInput2, 41),
|
|
(OptionsFocus::MidiInput3, 42),
|
|
// blank=43, ONBOARDING header=44, divider=45
|
|
(OptionsFocus::ResetOnboarding, 46),
|
|
(OptionsFocus::LoadDemoOnStartup, 47),
|
|
];
|
|
|
|
impl OptionsFocus {
|
|
fn is_plugin_only(self) -> bool {
|
|
PLUGIN_ONLY.contains(&self)
|
|
}
|
|
|
|
fn is_standalone_only(self) -> bool {
|
|
STANDALONE_ONLY.contains(&self)
|
|
}
|
|
|
|
fn is_visible(self, plugin_mode: bool) -> bool {
|
|
if self.is_plugin_only() && !plugin_mode {
|
|
return false;
|
|
}
|
|
if self.is_standalone_only() && plugin_mode {
|
|
return false;
|
|
}
|
|
true
|
|
}
|
|
|
|
pub fn line_index(self, plugin_mode: bool) -> usize {
|
|
visible_layout(plugin_mode)
|
|
.iter()
|
|
.find(|(f, _)| *f == self)
|
|
.map(|(_, l)| *l)
|
|
.unwrap_or(0)
|
|
}
|
|
|
|
pub fn at_line(line: usize, plugin_mode: bool) -> Option<OptionsFocus> {
|
|
visible_layout(plugin_mode)
|
|
.iter()
|
|
.find(|(_, l)| *l == line)
|
|
.map(|(f, _)| *f)
|
|
}
|
|
}
|
|
|
|
/// Total number of rendered lines for the options view.
|
|
pub fn total_lines(plugin_mode: bool) -> usize {
|
|
visible_layout(plugin_mode)
|
|
.last()
|
|
.map(|(_, l)| *l + 1)
|
|
.unwrap_or(0)
|
|
}
|
|
|
|
/// Compute (focus, line_index) pairs for only the visible options,
|
|
/// with line indices adjusted to account for hidden sections.
|
|
fn visible_layout(plugin_mode: bool) -> Vec<(OptionsFocus, usize)> {
|
|
// Start from the full layout and compute adjusted line numbers.
|
|
// Hidden items + their section headers/dividers/blanks shrink the layout.
|
|
|
|
// We know the exact section structure, so we compute the offset to subtract
|
|
// based on which sections are hidden.
|
|
let mut offset: usize = 0;
|
|
|
|
// Font/Zoom/Window lines (14,15,16) hidden when !plugin_mode
|
|
if !plugin_mode {
|
|
offset += 3; // 3 lines for Font, ZoomFactor, WindowSize
|
|
}
|
|
|
|
// Link + Session + MIDI sections hidden when plugin_mode
|
|
// These span from blank(17) through MidiInput3(42) = 26 lines
|
|
if plugin_mode {
|
|
let link_section_lines = 26;
|
|
offset += link_section_lines;
|
|
}
|
|
|
|
let mut result = Vec::new();
|
|
for &(focus, raw_line) in FULL_LAYOUT {
|
|
if !focus.is_visible(plugin_mode) {
|
|
continue;
|
|
}
|
|
// Lines at or below index 13 (PerformanceMode) are never shifted
|
|
let adjusted = if raw_line <= 13 {
|
|
raw_line
|
|
} else if !plugin_mode && raw_line <= 16 {
|
|
// Font/Zoom/Window — these are hidden, skip
|
|
continue;
|
|
} else {
|
|
raw_line - offset
|
|
};
|
|
result.push((focus, adjusted));
|
|
}
|
|
result
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct OptionsState {
|
|
pub focus: OptionsFocus,
|
|
}
|
|
|
|
impl OptionsState {
|
|
pub fn next_focus(&mut self, plugin_mode: bool) {
|
|
let mut f = self.focus;
|
|
loop {
|
|
f = f.next();
|
|
if f.is_visible(plugin_mode) {
|
|
break;
|
|
}
|
|
}
|
|
self.focus = f;
|
|
}
|
|
|
|
pub fn prev_focus(&mut self, plugin_mode: bool) {
|
|
let mut f = self.focus;
|
|
loop {
|
|
f = f.prev();
|
|
if f.is_visible(plugin_mode) {
|
|
break;
|
|
}
|
|
}
|
|
self.focus = f;
|
|
}
|
|
}
|