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}