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, 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::ResetOnboarding, Self::LoadDemoOnStartup, ]; } const PLUGIN_ONLY: &[OptionsFocus] = &[ OptionsFocus::Font, OptionsFocus::ZoomFactor, OptionsFocus::WindowSize, ]; /// Section layout: header line, divider line, then option lines. 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, ONBOARDING header=18, divider=19 (OptionsFocus::ResetOnboarding, 20), (OptionsFocus::LoadDemoOnStartup, 21), ]; impl OptionsFocus { fn is_plugin_only(self) -> bool { PLUGIN_ONLY.contains(&self) } fn is_visible(self, plugin_mode: bool) -> bool { if self.is_plugin_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 { 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) } fn visible_layout(plugin_mode: bool) -> Vec<(OptionsFocus, usize)> { let mut offset: usize = 0; // Font/Zoom/Window lines (14,15,16) hidden when !plugin_mode if !plugin_mode { offset += 3; } let mut result = Vec::new(); for &(focus, raw_line) in FULL_LAYOUT { if !focus.is_visible(plugin_mode) { continue; } let adjusted = if raw_line <= 13 { raw_line } else if !plugin_mode && raw_line <= 16 { 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; } }