Skip to main content

dear_app/
config.rs

1use std::path::PathBuf;
2
3use dear_imgui_rs::{ConfigFlags, DockNodeFlags, WindowFlags};
4
5/// Optional extension contexts created with the application UI state.
6#[derive(Clone, Copy, Debug, Default)]
7pub struct AddOnsConfig {
8    pub with_implot: bool,
9    pub with_imnodes: bool,
10    pub with_implot3d: bool,
11}
12
13impl AddOnsConfig {
14    /// Enables every add-on compiled into this crate.
15    #[must_use]
16    pub const fn auto() -> Self {
17        Self {
18            with_implot: cfg!(feature = "implot"),
19            with_imnodes: cfg!(feature = "imnodes"),
20            with_implot3d: cfg!(feature = "implot3d"),
21        }
22    }
23}
24
25/// Complete configuration shared by [`crate::run_ui`], [`crate::run_frame`], and [`crate::run`].
26pub struct AppConfig {
27    pub window_title: String,
28    pub window_size: (f64, f64),
29    pub present_mode: wgpu::PresentMode,
30    pub clear_color: [f32; 4],
31    pub wgpu: WgpuConfig,
32    pub docking: DockingConfig,
33    pub addons: AddOnsConfig,
34    pub ini_filename: Option<PathBuf>,
35    pub restore_previous_geometry: bool,
36    pub redraw: RedrawMode,
37    pub io_config_flags: Option<ConfigFlags>,
38    pub theme: Option<Theme>,
39}
40
41impl Default for AppConfig {
42    fn default() -> Self {
43        Self {
44            window_title: format!("Dear ImGui App - {}", env!("CARGO_PKG_VERSION")),
45            window_size: (1280.0, 720.0),
46            present_mode: wgpu::PresentMode::Fifo,
47            clear_color: [0.1, 0.2, 0.3, 1.0],
48            wgpu: WgpuConfig::default(),
49            docking: DockingConfig::default(),
50            addons: AddOnsConfig::default(),
51            ini_filename: None,
52            restore_previous_geometry: true,
53            redraw: RedrawMode::Poll,
54            io_config_flags: None,
55            theme: None,
56        }
57    }
58}
59
60/// Adapter and device requirements used for every GPU generation.
61pub struct WgpuConfig {
62    pub backends: wgpu::Backends,
63    pub power_preference: wgpu::PowerPreference,
64    pub force_fallback_adapter: bool,
65    pub device_label: Option<String>,
66    pub required_features: wgpu::Features,
67    pub required_limits: wgpu::Limits,
68    pub memory_hints: wgpu::MemoryHints,
69}
70
71impl Default for WgpuConfig {
72    fn default() -> Self {
73        Self {
74            backends: wgpu::Backends::PRIMARY,
75            power_preference: wgpu::PowerPreference::HighPerformance,
76            force_fallback_adapter: false,
77            device_label: None,
78            required_features: wgpu::Features::empty(),
79            required_limits: wgpu::Limits::default(),
80            memory_hints: wgpu::MemoryHints::default(),
81        }
82    }
83}
84
85/// Curated WGPU adapter and limits profiles.
86#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
87pub enum WgpuPreset {
88    #[default]
89    Default,
90    HighPerformance,
91    LowPower,
92    Balanced,
93    DownlevelCompatible,
94    SoftwareFallback,
95}
96
97impl WgpuConfig {
98    #[must_use]
99    pub fn from_preset(preset: WgpuPreset) -> Self {
100        match preset {
101            WgpuPreset::Default => Self::default(),
102            WgpuPreset::HighPerformance => Self {
103                power_preference: wgpu::PowerPreference::HighPerformance,
104                memory_hints: wgpu::MemoryHints::Performance,
105                ..Self::default()
106            },
107            WgpuPreset::LowPower => Self {
108                power_preference: wgpu::PowerPreference::LowPower,
109                memory_hints: wgpu::MemoryHints::MemoryUsage,
110                ..Self::default()
111            },
112            WgpuPreset::Balanced => Self {
113                power_preference: wgpu::PowerPreference::None,
114                ..Self::default()
115            },
116            WgpuPreset::DownlevelCompatible => Self {
117                power_preference: wgpu::PowerPreference::None,
118                required_limits: wgpu::Limits::downlevel_defaults(),
119                ..Self::default()
120            },
121            WgpuPreset::SoftwareFallback => Self {
122                power_preference: wgpu::PowerPreference::None,
123                force_fallback_adapter: true,
124                required_limits: wgpu::Limits::downlevel_defaults(),
125                ..Self::default()
126            },
127        }
128    }
129}
130
131/// Optional docking and built-in dockspace behavior.
132///
133/// Docking is disabled by default. Each enabled variant states whether `dear-app` or the
134/// application owns the dockspace host window.
135#[derive(Default)]
136pub enum DockingConfig {
137    /// Do not enable Dear ImGui docking.
138    #[default]
139    Disabled,
140    /// Enable docking without drawing a dockspace host window.
141    ApplicationManaged { dockspace_flags: DockNodeFlags },
142    /// Enable docking and draw a full-viewport dockspace host window every frame.
143    FullViewport {
144        dockspace_flags: DockNodeFlags,
145        host_window_flags: WindowFlags,
146        host_window_name: String,
147    },
148}
149
150impl DockingConfig {
151    /// Enables docking while leaving dockspace creation to the application.
152    #[must_use]
153    pub fn application_managed() -> Self {
154        Self::ApplicationManaged {
155            dockspace_flags: DockNodeFlags::PASSTHRU_CENTRAL_NODE,
156        }
157    }
158
159    /// Enables docking with a built-in full-viewport dockspace.
160    #[must_use]
161    pub fn full_viewport() -> Self {
162        Self::FullViewport {
163            dockspace_flags: DockNodeFlags::PASSTHRU_CENTRAL_NODE,
164            host_window_flags: WindowFlags::NO_TITLE_BAR
165                | WindowFlags::NO_RESIZE
166                | WindowFlags::NO_MOVE
167                | WindowFlags::NO_COLLAPSE
168                | WindowFlags::NO_BRING_TO_FRONT_ON_FOCUS
169                | WindowFlags::NO_NAV_FOCUS,
170            host_window_name: "DockSpaceHost".to_owned(),
171        }
172    }
173
174    #[must_use]
175    pub const fn is_enabled(&self) -> bool {
176        !matches!(self, Self::Disabled)
177    }
178
179    pub(crate) fn dockspace_flags(&self) -> DockNodeFlags {
180        match self {
181            Self::Disabled => DockNodeFlags::empty(),
182            Self::ApplicationManaged { dockspace_flags }
183            | Self::FullViewport {
184                dockspace_flags, ..
185            } => DockNodeFlags::from_bits_retain(dockspace_flags.bits()),
186        }
187    }
188
189    pub(crate) fn full_viewport_host(&self) -> Option<(&str, WindowFlags)> {
190        match self {
191            Self::FullViewport {
192                host_window_flags,
193                host_window_name,
194                ..
195            } => Some((
196                host_window_name,
197                WindowFlags::from_bits_retain(host_window_flags.bits()),
198            )),
199            Self::Disabled | Self::ApplicationManaged { .. } => None,
200        }
201    }
202}
203
204#[derive(Clone, Copy, Debug)]
205pub enum RedrawMode {
206    Poll,
207    Wait,
208    WaitUntil { fps: f32 },
209}
210
211#[derive(Clone, Copy, Debug)]
212pub enum Theme {
213    Dark,
214    Light,
215    Classic,
216}
217
218#[cfg(test)]
219mod tests {
220    use super::{AppConfig, DockingConfig};
221
222    #[test]
223    fn default_app_does_not_enable_docking() {
224        assert!(!AppConfig::default().docking.is_enabled());
225    }
226
227    #[test]
228    fn docking_modes_assign_dockspace_ownership_explicitly() {
229        let application_managed = DockingConfig::application_managed();
230        let full_viewport = DockingConfig::full_viewport();
231
232        assert!(application_managed.is_enabled());
233        assert!(application_managed.full_viewport_host().is_none());
234        assert!(full_viewport.is_enabled());
235        assert!(full_viewport.full_viewport_host().is_some());
236    }
237}