234 lines
6.3 KiB
Rust
234 lines
6.3 KiB
Rust
use super::CyclicEnum;
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Default)]
|
|
pub enum OptionsFocus {
|
|
#[default]
|
|
ColorScheme,
|
|
HueRotation,
|
|
RefreshRate,
|
|
RuntimeHighlight,
|
|
ShowScope,
|
|
ShowSpectrum,
|
|
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::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::ShowCompletion, 8),
|
|
(OptionsFocus::ShowPreview, 9),
|
|
(OptionsFocus::PerformanceMode, 10),
|
|
(OptionsFocus::Font, 11),
|
|
(OptionsFocus::ZoomFactor, 12),
|
|
(OptionsFocus::WindowSize, 13),
|
|
// blank=14, ABLETON LINK header=15, divider=16
|
|
(OptionsFocus::LinkEnabled, 17),
|
|
(OptionsFocus::StartStopSync, 18),
|
|
(OptionsFocus::Quantum, 19),
|
|
// blank=20, SESSION header=21, divider=22, Tempo=23, Beat=24, Phase=25
|
|
// blank=26, MIDI OUTPUTS header=27, divider=28
|
|
(OptionsFocus::MidiOutput0, 29),
|
|
(OptionsFocus::MidiOutput1, 30),
|
|
(OptionsFocus::MidiOutput2, 31),
|
|
(OptionsFocus::MidiOutput3, 32),
|
|
// blank=33, MIDI INPUTS header=34, divider=35
|
|
(OptionsFocus::MidiInput0, 36),
|
|
(OptionsFocus::MidiInput1, 37),
|
|
(OptionsFocus::MidiInput2, 38),
|
|
(OptionsFocus::MidiInput3, 39),
|
|
// blank=40, ONBOARDING header=41, divider=42
|
|
(OptionsFocus::ResetOnboarding, 43),
|
|
(OptionsFocus::LoadDemoOnStartup, 44),
|
|
];
|
|
|
|
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 (11,12,13) 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(14) through MidiInput3(39) = 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 10 (PerformanceMode) are never shifted
|
|
let adjusted = if raw_line <= 10 {
|
|
raw_line
|
|
} else if !plugin_mode && raw_line <= 13 {
|
|
// 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;
|
|
}
|
|
}
|