Skip to main content

halley_config/layout/
types.rs

1use halley_core::cluster_layout::ClusterWorkspaceLayoutKind;
2use halley_core::viewport::FocusRing;
3use regex::Regex;
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub enum NodeBorderColorMode {
7    UseWindowActive,
8    UseWindowInactive,
9    UseWindowSecondaryActive,
10    UseWindowSecondaryInactive,
11}
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum NodeDisplayPolicy {
15    Off,
16    Hover,
17    Always,
18}
19
20#[derive(Clone, Copy, Debug, PartialEq)]
21pub enum NodeBackgroundColorMode {
22    Auto,
23    Theme,
24    Light,
25    Dark,
26    Fixed { r: f32, g: f32, b: f32 },
27}
28
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub enum ShapeStyle {
31    Square,
32    Squircle,
33}
34
35#[derive(Clone, Copy, Debug, PartialEq)]
36pub enum OverlayColorMode {
37    Auto,
38    Light,
39    Dark,
40    Fixed { r: f32, g: f32, b: f32 },
41}
42
43#[derive(Clone, Copy, Debug, Eq, PartialEq)]
44pub enum OverlayShape {
45    Square,
46    Rounded,
47}
48
49#[derive(Clone, Copy, Debug, Eq, PartialEq)]
50pub enum OverlayBorderSource {
51    Primary,
52    Secondary,
53}
54
55#[derive(Clone, Copy, Debug, Eq, PartialEq)]
56pub enum PinBadgeCorner {
57    TopLeft,
58    TopRight,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq)]
62pub struct PinsConfig {
63    pub corner: PinBadgeCorner,
64    pub color: OverlayColorMode,
65    pub background_color: OverlayColorMode,
66    pub size: f32,
67}
68
69impl Default for PinsConfig {
70    fn default() -> Self {
71        Self {
72            corner: PinBadgeCorner::TopRight,
73            color: OverlayColorMode::Auto,
74            background_color: OverlayColorMode::Auto,
75            size: 1.0,
76        }
77    }
78}
79
80#[derive(Clone, Copy, Debug, PartialEq)]
81pub struct OverlayStyleConfig {
82    pub background_color: OverlayColorMode,
83    pub text_color: OverlayColorMode,
84    pub error_color: OverlayColorMode,
85    pub shape: OverlayShape,
86    pub borders: bool,
87    pub border_source: OverlayBorderSource,
88}
89
90impl Default for OverlayStyleConfig {
91    fn default() -> Self {
92        Self {
93            background_color: OverlayColorMode::Auto,
94            text_color: OverlayColorMode::Auto,
95            error_color: OverlayColorMode::Fixed {
96                r: 0xfb as f32 / 255.0,
97                g: 0x49 as f32 / 255.0,
98                b: 0x34 as f32 / 255.0,
99            },
100            shape: OverlayShape::Square,
101            borders: true,
102            border_source: OverlayBorderSource::Primary,
103        }
104    }
105}
106
107#[derive(Clone, Copy, Debug, PartialEq)]
108pub struct DebugConfig {
109    pub overlay_fps: bool,
110    pub show_ring_when_resizing: bool,
111}
112
113impl Default for DebugConfig {
114    fn default() -> Self {
115        Self {
116            overlay_fps: false,
117            show_ring_when_resizing: true,
118        }
119    }
120}
121
122#[derive(Clone, Copy, Debug, PartialEq)]
123pub struct AnimationToggleConfig {
124    pub enabled: bool,
125}
126
127impl Default for AnimationToggleConfig {
128    fn default() -> Self {
129        Self { enabled: true }
130    }
131}
132
133#[derive(Clone, Copy, Debug, PartialEq)]
134pub struct TimedAnimationConfig {
135    pub enabled: bool,
136    pub duration_ms: u64,
137}
138
139impl TimedAnimationConfig {
140    pub const fn new(enabled: bool, duration_ms: u64) -> Self {
141        Self {
142            enabled,
143            duration_ms,
144        }
145    }
146}
147
148#[derive(Clone, Copy, Debug, Eq, PartialEq)]
149pub enum WindowCloseAnimationStyle {
150    Shrink,
151    Fade,
152}
153
154#[derive(Clone, Copy, Debug, PartialEq)]
155pub struct WindowCloseAnimationConfig {
156    pub enabled: bool,
157    pub duration_ms: u64,
158    pub style: WindowCloseAnimationStyle,
159}
160
161#[derive(Clone, Copy, Debug, PartialEq)]
162pub struct RaiseAnimationConfig {
163    pub enabled: bool,
164    pub duration_ms: u64,
165    pub scale: f32,
166    pub shadow_boost: f32,
167}
168
169impl RaiseAnimationConfig {
170    pub const fn new(enabled: bool, duration_ms: u64, scale: f32, shadow_boost: f32) -> Self {
171        Self {
172            enabled,
173            duration_ms,
174            scale,
175            shadow_boost,
176        }
177    }
178}
179
180impl WindowCloseAnimationConfig {
181    pub const fn new(enabled: bool, duration_ms: u64, style: WindowCloseAnimationStyle) -> Self {
182        Self {
183            enabled,
184            duration_ms,
185            style,
186        }
187    }
188}
189
190#[derive(Clone, Copy, Debug, PartialEq)]
191pub struct AnimationsConfig {
192    pub enabled: bool,
193    pub smooth_resize: TimedAnimationConfig,
194    pub maximize: TimedAnimationConfig,
195    pub fullscreen: TimedAnimationConfig,
196    pub window_close: WindowCloseAnimationConfig,
197    pub window_open: TimedAnimationConfig,
198    pub tile: TimedAnimationConfig,
199    pub stack: TimedAnimationConfig,
200    pub raise: RaiseAnimationConfig,
201}
202
203impl Default for AnimationsConfig {
204    fn default() -> Self {
205        Self {
206            enabled: true,
207            smooth_resize: TimedAnimationConfig::new(true, 90),
208            maximize: TimedAnimationConfig::new(true, 240),
209            fullscreen: TimedAnimationConfig::new(true, 240),
210            window_close: WindowCloseAnimationConfig::new(
211                true,
212                250,
213                WindowCloseAnimationStyle::Shrink,
214            ),
215            window_open: TimedAnimationConfig::new(true, 620),
216            tile: TimedAnimationConfig::new(true, 240),
217            stack: TimedAnimationConfig::new(true, 220),
218            raise: RaiseAnimationConfig::new(true, 140, 1.025, 0.18),
219        }
220    }
221}
222
223#[derive(Clone, Debug, PartialEq)]
224pub struct ScreenshotConfig {
225    pub directory: String,
226    pub highlight_color: OverlayColorMode,
227    pub background_color: OverlayColorMode,
228}
229
230impl Default for ScreenshotConfig {
231    fn default() -> Self {
232        let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
233        Self {
234            directory: format!("{home}/Pictures/Screenshots"),
235            highlight_color: OverlayColorMode::Auto,
236            background_color: OverlayColorMode::Auto,
237        }
238    }
239}
240
241#[derive(Clone, Copy, Debug, PartialEq)]
242pub struct DecorationBorderColor {
243    pub r: f32,
244    pub g: f32,
245    pub b: f32,
246}
247
248#[derive(Clone, Copy, Debug, PartialEq)]
249pub struct PrimaryBorderConfig {
250    pub size_px: i32,
251    pub radius_px: i32,
252    pub color_focused: DecorationBorderColor,
253    pub color_unfocused: DecorationBorderColor,
254}
255
256#[derive(Clone, Copy, Debug, PartialEq)]
257pub struct SecondaryBorderConfig {
258    pub enabled: bool,
259    pub size_px: i32,
260    pub gap_px: i32,
261    pub color_focused: DecorationBorderColor,
262    pub color_unfocused: DecorationBorderColor,
263}
264
265#[derive(Clone, Copy, Debug, PartialEq)]
266pub struct ShadowColor {
267    pub r: f32,
268    pub g: f32,
269    pub b: f32,
270    pub a: f32,
271}
272
273#[derive(Clone, Copy, Debug, PartialEq)]
274pub struct ShadowLayerConfig {
275    pub enabled: bool,
276    pub blur_radius: f32,
277    pub spread: f32,
278    pub offset_x: f32,
279    pub offset_y: f32,
280    pub color: ShadowColor,
281}
282
283#[derive(Clone, Copy, Debug, PartialEq)]
284pub struct ShadowsConfig {
285    pub window: ShadowLayerConfig,
286    pub node: ShadowLayerConfig,
287    pub overlay: ShadowLayerConfig,
288}
289
290#[derive(Clone, Copy, Debug, PartialEq)]
291pub struct DecorationsConfig {
292    pub border: PrimaryBorderConfig,
293    pub secondary_border: SecondaryBorderConfig,
294    pub shadows: ShadowsConfig,
295    pub resize_using_border: bool,
296}
297
298impl Default for PrimaryBorderConfig {
299    fn default() -> Self {
300        Self {
301            size_px: 3,
302            radius_px: 0,
303            color_focused: DecorationBorderColor {
304                r: 0.22,
305                g: 0.82,
306                b: 0.92,
307            },
308            color_unfocused: DecorationBorderColor {
309                r: 0.28,
310                g: 0.30,
311                b: 0.35,
312            },
313        }
314    }
315}
316
317impl Default for SecondaryBorderConfig {
318    fn default() -> Self {
319        Self {
320            enabled: false,
321            size_px: 1,
322            gap_px: 2,
323            color_focused: DecorationBorderColor {
324                r: 0.98,
325                g: 0.74,
326                b: 0.15,
327            },
328            color_unfocused: DecorationBorderColor {
329                r: 0.12,
330                g: 0.12,
331                b: 0.12,
332            },
333        }
334    }
335}
336
337impl Default for ShadowsConfig {
338    fn default() -> Self {
339        Self {
340            window: ShadowLayerConfig {
341                enabled: true,
342                blur_radius: 8.0,
343                spread: 0.0,
344                offset_x: 0.0,
345                offset_y: 5.0,
346                color: ShadowColor {
347                    r: 0x05 as f32 / 255.0,
348                    g: 0x03 as f32 / 255.0,
349                    b: 0x05 as f32 / 255.0,
350                    a: 0x30 as f32 / 255.0,
351                },
352            },
353            node: ShadowLayerConfig {
354                enabled: true,
355                blur_radius: 14.0,
356                spread: 0.0,
357                offset_x: 0.0,
358                offset_y: 3.0,
359                color: ShadowColor {
360                    r: 0x05 as f32 / 255.0,
361                    g: 0x03 as f32 / 255.0,
362                    b: 0x05 as f32 / 255.0,
363                    a: 0x24 as f32 / 255.0,
364                },
365            },
366            overlay: ShadowLayerConfig {
367                enabled: true,
368                blur_radius: 24.0,
369                spread: 1.0,
370                offset_x: 0.0,
371                offset_y: 7.0,
372                color: ShadowColor {
373                    r: 0x05 as f32 / 255.0,
374                    g: 0x03 as f32 / 255.0,
375                    b: 0x05 as f32 / 255.0,
376                    a: 0x38 as f32 / 255.0,
377                },
378            },
379        }
380    }
381}
382
383impl Default for DecorationsConfig {
384    fn default() -> Self {
385        Self {
386            border: PrimaryBorderConfig::default(),
387            secondary_border: SecondaryBorderConfig::default(),
388            shadows: ShadowsConfig::default(),
389            resize_using_border: false,
390        }
391    }
392}
393
394#[derive(Clone, Copy, Debug, Eq, PartialEq)]
395pub enum PanToNewMode {
396    Never,
397    IfNeeded,
398    Always,
399}
400
401#[derive(Clone, Copy, Debug, Eq, PartialEq)]
402pub enum ExpandedPlacementStrategy {
403    Center,
404    FindEmpty,
405}
406
407#[derive(Clone, Copy, Debug, Eq, PartialEq)]
408pub enum FindEmptyMode {
409    BestEffort,
410}
411
412#[derive(Clone, Copy, Debug, Eq, PartialEq)]
413pub enum LandmarkPlacementStrategy {
414    NearestFree,
415}
416
417#[derive(Clone, Copy, Debug, Eq, PartialEq)]
418pub enum NormalBlockerPolicy {
419    Relocate,
420}
421
422#[derive(Clone, Copy, Debug, Eq, PartialEq)]
423pub enum PinnedBlockerPolicy {
424    Preserve,
425}
426
427#[derive(Clone, Debug, Eq, PartialEq)]
428pub struct ExpandedPlacementConfig {
429    pub strategy: ExpandedPlacementStrategy,
430    pub fallback: ExpandedPlacementStrategy,
431    pub find_empty_mode: FindEmptyMode,
432}
433
434impl Default for ExpandedPlacementConfig {
435    fn default() -> Self {
436        Self {
437            strategy: ExpandedPlacementStrategy::FindEmpty,
438            fallback: ExpandedPlacementStrategy::Center,
439            find_empty_mode: FindEmptyMode::BestEffort,
440        }
441    }
442}
443
444#[derive(Clone, Debug, Eq, PartialEq)]
445pub struct LandmarkPlacementConfig {
446    pub strategy: LandmarkPlacementStrategy,
447    pub normal_blocker: NormalBlockerPolicy,
448    pub pinned_blocker: PinnedBlockerPolicy,
449}
450
451impl Default for LandmarkPlacementConfig {
452    fn default() -> Self {
453        Self {
454            strategy: LandmarkPlacementStrategy::NearestFree,
455            normal_blocker: NormalBlockerPolicy::Relocate,
456            pinned_blocker: PinnedBlockerPolicy::Preserve,
457        }
458    }
459}
460
461#[derive(Clone, Debug, PartialEq)]
462pub struct PlacementRevealConfig {
463    pub enabled: bool,
464    pub max_pan_px: f32,
465    pub animation_ms: u64,
466}
467
468impl Default for PlacementRevealConfig {
469    fn default() -> Self {
470        Self {
471            enabled: true,
472            max_pan_px: 360.0,
473            animation_ms: 180,
474        }
475    }
476}
477
478#[derive(Clone, Debug, PartialEq)]
479pub struct PlacementConfig {
480    pub expanded: ExpandedPlacementConfig,
481    pub landmarks: LandmarkPlacementConfig,
482    pub reveal: PlacementRevealConfig,
483}
484
485impl Default for PlacementConfig {
486    fn default() -> Self {
487        Self {
488            expanded: ExpandedPlacementConfig::default(),
489            landmarks: LandmarkPlacementConfig::default(),
490            reveal: PlacementRevealConfig::default(),
491        }
492    }
493}
494
495#[derive(Clone, Copy, Debug, Eq, PartialEq)]
496pub enum CloseRestorePanMode {
497    Never,
498    IfOffscreen,
499    Always,
500}
501
502#[derive(Clone, Copy, Debug, Eq, PartialEq)]
503pub enum ClickCollapsedOutsideFocusMode {
504    Ignore,
505    Activate,
506}
507
508#[derive(Clone, Copy, Debug, Eq, PartialEq)]
509pub enum ClickCollapsedPanMode {
510    Never,
511    IfOffscreen,
512    Always,
513}
514
515#[derive(Clone, Copy, Debug, Eq, PartialEq)]
516pub enum InputFocusMode {
517    Click,
518    Hover,
519}
520
521#[derive(Clone, Debug, Eq, PartialEq)]
522pub struct KeyboardConfig {
523    pub layout: String,
524    pub variant: String,
525    pub options: String,
526}
527
528impl Default for KeyboardConfig {
529    fn default() -> Self {
530        Self {
531            layout: "us".to_string(),
532            variant: String::new(),
533            options: String::new(),
534        }
535    }
536}
537
538#[derive(Clone, Debug, Eq, PartialEq)]
539pub struct InputConfig {
540    pub repeat_rate: i32,
541    pub repeat_delay: i32,
542    pub focus_mode: InputFocusMode,
543    pub raise_on_click: bool,
544    pub keyboard: KeyboardConfig,
545}
546
547impl Default for InputConfig {
548    fn default() -> Self {
549        Self {
550            repeat_rate: 30,
551            repeat_delay: 500,
552            focus_mode: InputFocusMode::Click,
553            raise_on_click: true,
554            keyboard: KeyboardConfig::default(),
555        }
556    }
557}
558
559#[derive(Clone, Copy, Debug, PartialEq)]
560pub struct FocusRingConfig {
561    pub rx: f32,
562    pub ry: f32,
563    pub offset_x: f32,
564    pub offset_y: f32,
565}
566
567impl FocusRingConfig {
568    pub fn to_focus_ring(self) -> FocusRing {
569        FocusRing::new(self.rx, self.ry, self.offset_x, self.offset_y)
570    }
571}
572
573#[derive(Clone, Copy, Debug, PartialEq)]
574pub struct BearingsConfig {
575    pub show_distance: bool,
576    pub show_icons: bool,
577    pub show_pinned: bool,
578    pub fade_distance: f32,
579}
580
581#[derive(Clone, Copy, Debug, Eq, PartialEq)]
582pub enum RailPlacement {
583    Up,
584    Down,
585    Left,
586    Right,
587}
588
589#[derive(Clone, Copy, Debug, Eq, PartialEq)]
590pub enum RailSizingMode {
591    Fixed,
592    GrowToContent,
593}
594
595#[derive(Clone, Copy, Debug, Eq, PartialEq)]
596pub enum RailObstructionBehavior {
597    AutoHide,
598    StayOnTop,
599    StayUnder,
600}
601
602#[derive(Clone, Copy, Debug, PartialEq)]
603pub struct RailConfig {
604    pub enabled: bool,
605    pub placement: RailPlacement,
606    pub background_color: OverlayColorMode,
607    pub foreground_color: OverlayColorMode,
608    pub divider_color: OverlayColorMode,
609    pub offset_x: i32,
610    pub offset_y: i32,
611    pub width: i32,
612    pub height: i32,
613    pub sizing: RailSizingMode,
614    pub icon_size: i32,
615    pub gap: i32,
616    pub padding: i32,
617    pub radius: i32,
618    pub pinned_separator: bool,
619    pub obstruction: RailObstructionBehavior,
620}
621
622impl Default for RailConfig {
623    fn default() -> Self {
624        Self {
625            enabled: true,
626            placement: RailPlacement::Down,
627            background_color: OverlayColorMode::Auto,
628            foreground_color: OverlayColorMode::Auto,
629            divider_color: OverlayColorMode::Auto,
630            offset_x: 0,
631            offset_y: 18,
632            width: 0,
633            height: 56,
634            sizing: RailSizingMode::GrowToContent,
635            icon_size: 34,
636            gap: 8,
637            padding: 10,
638            radius: 18,
639            pinned_separator: true,
640            obstruction: RailObstructionBehavior::AutoHide,
641        }
642    }
643}
644
645#[derive(Clone, Debug, Eq, PartialEq)]
646pub struct CursorConfig {
647    pub theme: String,
648    pub size: u32,
649    pub hide_while_typing: bool,
650    pub hide_after_ms: u64,
651}
652
653impl Default for CursorConfig {
654    fn default() -> Self {
655        Self {
656            theme: "Adwaita".to_string(),
657            size: 24,
658            hide_while_typing: false,
659            hide_after_ms: 0,
660        }
661    }
662}
663
664#[derive(Clone, Debug, Eq, PartialEq)]
665pub struct FontConfig {
666    pub family: String,
667    pub size: u32,
668}
669
670impl Default for FontConfig {
671    fn default() -> Self {
672        Self {
673            family: "monospace".to_string(),
674            size: 11,
675        }
676    }
677}
678
679#[derive(Clone, Copy, Debug, Eq, PartialEq)]
680pub enum ClusterBloomDirection {
681    Clockwise,
682    CounterClockwise,
683}
684
685#[derive(Clone, Copy, Debug, Eq, PartialEq)]
686pub enum ClusterDefaultLayout {
687    Tiling,
688    Stacking,
689}
690
691impl ClusterDefaultLayout {
692    pub fn to_workspace_layout_kind(self) -> ClusterWorkspaceLayoutKind {
693        match self {
694            Self::Tiling => ClusterWorkspaceLayoutKind::Tiling,
695            Self::Stacking => ClusterWorkspaceLayoutKind::Stacking,
696        }
697    }
698}
699
700#[derive(Clone, Debug)]
701pub enum WindowRulePattern {
702    Exact(String),
703    Regex(Regex),
704}
705
706impl PartialEq for WindowRulePattern {
707    fn eq(&self, other: &Self) -> bool {
708        match (self, other) {
709            (Self::Exact(a), Self::Exact(b)) => a == b,
710            (Self::Regex(a), Self::Regex(b)) => a.as_str() == b.as_str(),
711            _ => false,
712        }
713    }
714}
715
716impl Eq for WindowRulePattern {}
717
718impl WindowRulePattern {
719    pub fn matches(&self, value: &str) -> bool {
720        match self {
721            Self::Exact(exact) => exact == value,
722            Self::Regex(regex) => regex.is_match(value),
723        }
724    }
725
726    pub fn as_str(&self) -> &str {
727        match self {
728            Self::Exact(exact) => exact.as_str(),
729            Self::Regex(regex) => regex.as_str(),
730        }
731    }
732}
733
734#[derive(Clone, Copy, Debug, Eq, PartialEq)]
735pub enum InitialWindowOverlapPolicy {
736    None,
737    ParentOnly,
738    All,
739}
740
741#[derive(Clone, Copy, Debug, Eq, PartialEq)]
742pub enum InitialWindowSpawnPlacement {
743    Default,
744    Center,
745    Adjacent,
746    ViewportCenter,
747    Cursor,
748    App,
749}
750
751#[derive(Clone, Copy, Debug, Eq, PartialEq)]
752pub enum InitialWindowClusterParticipation {
753    Layout,
754    Float,
755}
756
757#[derive(Clone, Debug, PartialEq)]
758pub struct WindowRule {
759    pub app_ids: Vec<WindowRulePattern>,
760    pub titles: Vec<WindowRulePattern>,
761    pub initial_size: Option<(u32, u32)>,
762    pub opacity: Option<f32>,
763    pub overlap_policy: InitialWindowOverlapPolicy,
764    pub spawn_placement: InitialWindowSpawnPlacement,
765    pub cluster_participation: InitialWindowClusterParticipation,
766}
767
768#[derive(Clone, Debug, PartialEq)]
769pub struct ViewportOutputConfig {
770    pub connector: String,
771    pub enabled: bool,
772    pub offset_x: i32,
773    pub offset_y: i32,
774    pub width: u32,
775    pub height: u32,
776    pub refresh_rate: Option<f64>,
777    pub transform_degrees: u16,
778    pub vrr: ViewportVrrMode,
779    pub focus_ring: Option<FocusRingConfig>,
780}
781
782#[derive(Clone, Copy, Debug, PartialEq, Eq)]
783pub enum ViewportVrrMode {
784    Off,
785    On,
786    OnDemand,
787}
788
789impl ViewportVrrMode {
790    pub fn as_str(self) -> &'static str {
791        match self {
792            Self::Off => "off",
793            Self::On => "on",
794            Self::OnDemand => "on-demand",
795        }
796    }
797
798    pub fn drm_enabled(self) -> bool {
799        matches!(self, Self::On)
800    }
801}