hyprshell_windows_lib/switch/
create.rs

1use crate::global::{WindowsSwitchConfig, WindowsSwitchData};
2use adw::gtk::gdk::Key;
3use adw::gtk::glib::Propagation;
4use adw::gtk::prelude::*;
5use adw::gtk::{
6    Application, ApplicationWindow, EventControllerKey, FlowBox, Orientation, Overlay,
7    SelectionMode,
8};
9use anyhow::Context;
10use async_channel::Sender;
11use config_lib::{FilterBy, Modifier, Switch, Windows};
12use core_lib::transfer::{Direction, SwitchSwitchConfig, TransferType};
13use core_lib::{HyprlandData, SWITCH_NAMESPACE, WarnWithDetails};
14use exec_lib::get_initial_active;
15use gtk4_layer_shell::{KeyboardMode, Layer, LayerShell};
16use std::collections::HashMap;
17use tracing::{debug, debug_span};
18
19pub fn create_windows_switch_window(
20    app: &Application,
21    switch: &Switch,
22    windows: &Windows,
23    event_sender: Sender<TransferType>,
24) -> anyhow::Result<WindowsSwitchData> {
25    let _span = debug_span!("create_windows_switch_window").entered();
26
27    let clients_flow = FlowBox::builder()
28        .selection_mode(SelectionMode::None)
29        .orientation(Orientation::Horizontal)
30        .max_children_per_line(u32::from(windows.items_per_row))
31        .min_children_per_line(u32::from(windows.items_per_row))
32        .build();
33
34    let clients_flow_overlay = Overlay::builder()
35        .child(&clients_flow)
36        .css_classes(["monitor", "no-hover"])
37        .build();
38
39    let window = ApplicationWindow::builder()
40        .css_classes(["window"])
41        .application(app)
42        .child(&clients_flow_overlay)
43        .default_height(10)
44        .default_width(10)
45        .build();
46
47    let key_controller = EventControllerKey::new();
48    let event_sender_2 = event_sender.clone();
49    key_controller.connect_key_pressed(move |_, key, _, _| handle_key(key, &event_sender_2));
50    let event_sender_3 = event_sender;
51    let r#mod = switch.modifier;
52    key_controller.connect_key_released(move |_, key, _, _| {
53        handle_release(key, r#mod, &event_sender_3);
54    });
55    window.add_controller(key_controller);
56
57    window.init_layer_shell();
58    window.set_namespace(Some(SWITCH_NAMESPACE));
59    window.set_layer(Layer::Top);
60    // we only have one window, so we can do this
61    // we also don't use adw::gtk::Popover which doesnt work with exclusive mode
62    window.set_keyboard_mode(KeyboardMode::Exclusive);
63    window.present();
64    window.set_visible(false);
65
66    debug!("Created switch window ({})", window.id());
67
68    Ok(WindowsSwitchData {
69        config: WindowsSwitchConfig {
70            items_per_row: windows.items_per_row,
71            scale: windows.scale,
72            filter_current_workspace: switch.filter_by.contains(&FilterBy::CurrentWorkspace),
73            filter_current_monitor: switch.filter_by.contains(&FilterBy::CurrentMonitor),
74            filter_same_class: switch.filter_by.contains(&FilterBy::SameClass),
75            switch_workspaces: switch.switch_workspaces,
76        },
77        window,
78        main_flow: clients_flow,
79        workspaces: HashMap::default(),
80        clients: HashMap::default(),
81        active: get_initial_active().context("unable to get initial active data")?,
82        hypr_data: HyprlandData::default(),
83    })
84}
85
86fn handle_release(key: Key, modifier: Modifier, event_sender: &Sender<TransferType>) {
87    if ((key == Key::Alt_L || key == Key::Alt_R) && modifier == Modifier::Alt)
88        || ((key == Key::Control_L || key == Key::Control_R) && modifier == Modifier::Ctrl)
89        || ((key == Key::Super_L || key == Key::Super_R) && modifier == Modifier::Super)
90    {
91        event_sender
92            .send_blocking(TransferType::CloseSwitch)
93            .warn_details("unable to send");
94    }
95}
96
97fn handle_key(key: Key, event_sender: &Sender<TransferType>) -> Propagation {
98    match key {
99        Key::Tab | Key::l | Key::Right => {
100            event_sender
101                .send_blocking(TransferType::SwitchSwitch(SwitchSwitchConfig {
102                    direction: Direction::Right,
103                }))
104                .warn_details("unable to send");
105            Propagation::Stop
106        }
107        Key::ISO_Left_Tab | Key::grave | Key::dead_grave | Key::h | Key::Left => {
108            event_sender
109                .send_blocking(TransferType::SwitchSwitch(SwitchSwitchConfig {
110                    direction: Direction::Left,
111                }))
112                .warn_details("unable to send");
113            Propagation::Stop
114        }
115        Key::j | Key::Down => {
116            event_sender
117                .send_blocking(TransferType::SwitchSwitch(SwitchSwitchConfig {
118                    direction: Direction::Down,
119                }))
120                .warn_details("unable to send");
121            Propagation::Stop
122        }
123        Key::k | Key::Up => {
124            event_sender
125                .send_blocking(TransferType::SwitchSwitch(SwitchSwitchConfig {
126                    direction: Direction::Up,
127                }))
128                .warn_details("unable to send");
129            Propagation::Stop
130        }
131        _ => Propagation::Proceed,
132    }
133}