pub trait CyclicEnum: Sized + Copy + PartialEq + 'static {
    const VARIANTS: &'static [Self];

    fn next(self) -> Self {
        let pos = Self::VARIANTS.iter().position(|v| *v == self).unwrap_or(0);
        Self::VARIANTS[(pos + 1) % Self::VARIANTS.len()]
    }

    fn prev(self) -> Self {
        let len = Self::VARIANTS.len();
        let pos = Self::VARIANTS.iter().position(|v| *v == self).unwrap_or(0);
        Self::VARIANTS[(pos + len - 1) % len]
    }
}

pub mod audio;
pub mod color_scheme;
pub mod editor;
pub mod effects;
pub mod file_browser;
pub mod live_keys;
pub mod modal;
pub mod mute;
pub mod options;
pub mod panel;
pub mod patterns_nav;
pub mod playback;
pub mod project;
pub mod sample_browser;
pub mod undo;
pub mod ui;

pub use audio::{AudioSettings, DeviceKind, EngineSection, MainLayout, Metrics, ScopeMode, SettingKind, SpectrumMode};
pub use color_scheme::ColorScheme;
pub use editor::{
    CopiedStepData, CopiedSteps, EditorContext, EditorTarget, EuclideanField, PatternField,
    PatternPropsField, ScriptEditorState, ScriptField, StackCache,
};
pub use live_keys::LiveKeyState;
pub use modal::{ConfirmAction, Modal, RenameTarget};
pub use options::{OptionsFocus, OptionsState};
pub use panel::{PanelFocus, PanelState, SidePanel};
pub use patterns_nav::{PatternsColumn, PatternsNav};
pub use mute::MuteState;
pub use playback::{PlaybackState, StagedChange, StagedMuteChange, StagedPropChange};
pub use project::ProjectState;
pub use sample_browser::{SampleBrowserState, SampleTree};
pub use ui::{DictFocus, FlashKind, HelpFocus, MinimapMode, UiState};
