Skip to main content

hyprshell_config_edit_lib/
util.rs

1use crate::structs::ConfigModifier;
2use relm4::gtk::gdk::{Cursor, Key, ModifierType};
3use relm4::gtk::prelude::{Cast, EditableExt, WidgetExt};
4use relm4::{adw, gtk};
5// use relm4::tokio::time::sleep;
6use tracing::{instrument, warn};
7
8pub trait SetTextIfDifferent {
9    fn set_text_if_different(&self, text: &str);
10}
11
12impl SetTextIfDifferent for gtk::Entry {
13    fn set_text_if_different(&self, text: &str) {
14        use relm4::adw::prelude::EditableExt;
15        if self.text() != text {
16            self.set_text(text);
17        }
18    }
19}
20
21impl SetTextIfDifferent for adw::EntryRow {
22    fn set_text_if_different(&self, text: &str) {
23        if self.text() != text {
24            self.set_text(text);
25        }
26    }
27}
28
29pub trait SetCursor {
30    fn set_cursor_by_name(&self, name: &str);
31}
32
33impl SetCursor for gtk::Image {
34    fn set_cursor_by_name(&self, name: &str) {
35        use relm4::adw::prelude::WidgetExt;
36        self.set_cursor(Cursor::from_name(name, None).as_ref());
37    }
38}
39
40pub trait ScrollToPosition {
41    fn scroll_to_pos(&self, pos: usize, animate: bool);
42}
43
44impl ScrollToPosition for adw::Carousel {
45    fn scroll_to_pos(&self, pos: usize, animate: bool) {
46        if let Some(wdg) = self.observe_children().into_iter().flatten().nth(pos)
47            && let Ok(widget) = wdg.downcast::<gtk::Widget>()
48        {
49            let s2 = self.clone();
50            // scuffed method to select a new widget (else it doesn't work on the first render)
51            gtk::glib::idle_add_local(move || {
52                s2.scroll_to(&widget, animate);
53                #[allow(clippy::cast_sign_loss)]
54                if s2.position() as usize == pos {
55                    gtk::glib::ControlFlow::Break
56                } else {
57                    gtk::glib::ControlFlow::Continue
58                }
59            });
60        }
61    }
62}
63
64#[allow(dead_code)]
65pub trait SelectRow {
66    fn select_row_index_opt(&self, index: Option<i32>);
67    fn select_row_index(&self, index: i32);
68}
69
70impl SelectRow for gtk::ListBox {
71    fn select_row_index_opt(&self, index: Option<i32>) {
72        self.unselect_all();
73        if let Some(index) = index {
74            self.select_row_index(index);
75        }
76    }
77
78    fn select_row_index(&self, index: i32) {
79        self.unselect_all();
80        if self.selected_row() != self.row_at_index(index) {
81            if let Some(row) = self.row_at_index(index) {
82                self.select_row(Some(&row));
83            } else {
84                warn!("select_row_index: row not found ({index})");
85            }
86        }
87    }
88}
89
90pub fn handle_key(val: Key, state: ModifierType) -> Option<(String, ConfigModifier, String)> {
91    let key_name = val.name()?;
92    let modifier = match val {
93        Key::Alt_L | Key::Alt_R => ConfigModifier::Alt,
94        Key::Control_L | Key::Control_R => ConfigModifier::Ctrl,
95        Key::Super_L | Key::Super_R => ConfigModifier::Super,
96        _ => match state {
97            ModifierType::NO_MODIFIER_MASK => ConfigModifier::None,
98            ModifierType::ALT_MASK => ConfigModifier::Alt,
99            ModifierType::CONTROL_MASK => ConfigModifier::Ctrl,
100            ModifierType::SUPER_MASK => ConfigModifier::Super,
101            _ => return None,
102        },
103    };
104
105    let label = if modifier == ConfigModifier::None {
106        key_name.to_string()
107    } else {
108        format!("{modifier} + {key_name}")
109    };
110
111    Some((key_name.to_string(), modifier, label))
112}
113
114pub fn default_config() -> config_lib::Config {
115    config_lib::Config {
116        windows: Some(config_lib::Windows::default()),
117    }
118}
119
120#[instrument(level = "trace", ret(level = "trace"))]
121pub fn mod_key_to_accelerator(modifier: ConfigModifier, key: &str) -> String {
122    // correct some keys that can sometimes have wrong capitalization
123    let key = match &*key.to_lowercase() {
124        "super_l" => "Super_L",
125        "super_r" => "Super_R",
126        "alt_l" => "Alt_L",
127        "alt_r" => "Alt_R",
128        "control_l" => "Control_L",
129        "control_r" => "Control_R",
130        _ => key,
131    };
132
133    if modifier == ConfigModifier::None {
134        key.to_string()
135    } else {
136        format!("<{modifier}>{key}")
137    }
138}
139
140#[instrument(level = "trace", ret(level = "trace"))]
141pub fn mod_key_to_string(modifier: ConfigModifier, key: &str) -> String {
142    if modifier == ConfigModifier::None {
143        key.to_string()
144    } else {
145        format!("{modifier} + {key}")
146    }
147}
148
149#[macro_export]
150macro_rules! flags_csv {
151    ($s:expr, $($field:ident),+ $(,)?) => {{
152        [$( (stringify!($field), $s.$field) ),+]
153            .into_iter()
154            .filter(|(_, v)| *v)
155            .map(|(k, _)| k)
156            .collect::<Vec<&str>>()
157            .join(", ")
158    }};
159}