use crate::global::{LauncherConfig, LauncherData};
use crate::plugins;
use crate::plugins::get_static_options_chars;
use async_channel::Sender;
use config_lib::{Launcher, Modifier};
use core_lib::transfer::{CloseOverviewConfig, Direction, SwitchOverviewConfig, TransferType};
use core_lib::{LAUNCHER_NAMESPACE, WarnWithDetails};
use gtk4_layer_shell::{Edge, Layer, LayerShell};
use relm4::adw::gtk::gdk::{Key, ModifierType};
use relm4::adw::gtk::glib::{ControlFlow, Propagation};
use relm4::adw::gtk::prelude::*;
use relm4::adw::gtk::{
Application, ApplicationWindow, Entry, EventControllerKey, ListBox, PropagationPhase,
SelectionMode,
};
use relm4::adw::gtk::{Orientation, glib};
use relm4::gtk;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use tracing::{debug, debug_span, trace};
pub fn create_windows_overview_launcher_window(
app: &Application,
launcher: &Launcher,
data_dir: &Path,
event_sender: &Sender<TransferType>,
) -> anyhow::Result<LauncherData> {
let _span = debug_span!("create_windows_overview_launcher_window").entered();
let main_vbox = ListBox::builder()
.css_classes(["launcher"])
.width_request(i32::try_from(launcher.width)?)
.selection_mode(SelectionMode::None)
.build();
let entry = Entry::builder().css_classes(["launcher-input"]).build();
let event_sender_2 = event_sender.clone();
entry.connect_changed(move |e| {
launcher_entry_text_change(e.text().to_string(), &event_sender_2);
});
main_vbox.append(&entry);
let results = gtk::Box::builder()
.orientation(Orientation::Vertical)
.css_classes(["launcher-results"])
.spacing(3)
.build();
main_vbox.append(&results);
let plugin_box = gtk::Box::builder()
.orientation(Orientation::Horizontal)
.css_classes(["launcher-plugins"])
.spacing(4)
.build();
main_vbox.append(&plugin_box);
let window = ApplicationWindow::builder()
.css_classes(["window"])
.application(app)
.child(&main_vbox)
.default_height(10)
.default_width(10)
.build();
window.init_layer_shell();
window.set_namespace(Some(LAUNCHER_NAMESPACE));
window.set_layer(Layer::Top);
window.set_anchor(Edge::Top, true);
window.set_margin(Edge::Top, 0);
window.set_exclusive_zone(-1);
let event_controller = EventControllerKey::new();
let plugin_keys = get_static_options_chars(&launcher.plugins);
let event_sender_4 = event_sender.clone();
let entry_2 = entry.clone();
let results_2 = results.clone();
let launch_modifier = launcher.launch_modifier;
event_controller.connect_key_pressed(move |_, key, _, modt| {
trace!("input: {key:?}");
handle_key(
&entry_2,
key,
modt,
&plugin_keys,
launch_modifier,
&results_2,
&event_sender_4,
)
});
event_controller.set_propagation_phase(PropagationPhase::Capture);
entry.add_controller(event_controller);
let entry_2 = entry.clone();
let window_2 = window.clone();
glib::timeout_add_local(std::time::Duration::from_millis(200), move || {
if window_2.is_visible() {
entry_2.grab_focus_without_selecting(); }
ControlFlow::Continue
});
debug!("Created launcher window ({})", window.id());
launcher_entry_text_change(String::new(), event_sender);
plugins::init_calc_context();
Ok(LauncherData {
config: LauncherConfig {
default_terminal: launcher.default_terminal.clone(),
max_items: launcher.max_items,
launch_modifier: launcher.launch_modifier,
show_when_empty: launcher.show_when_empty,
width: launcher.width,
data_dir: PathBuf::from(data_dir).into_boxed_path(),
plugins: launcher.plugins.clone(),
},
window,
entry,
results_box: results,
results_items: HashMap::new(),
plugins_box: plugin_box,
plugins_items: HashMap::new(),
sorted_matches: vec![],
static_matches: HashMap::new(),
})
}
fn launcher_entry_text_change(text: String, event_sender: &Sender<TransferType>) {
event_sender
.send_blocking(TransferType::Type(text))
.warn_details("unable to send");
}
#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
fn handle_key(
entry: &Entry,
key: Key,
modt: ModifierType,
plugin_keys: &[Key],
launch_modifier: Modifier,
results: >k::Box,
event_sender: &Sender<TransferType>,
) -> Propagation {
let launch_mod = match launch_modifier {
Modifier::Ctrl => modt == ModifierType::CONTROL_MASK,
Modifier::Alt => modt == ModifierType::ALT_MASK,
Modifier::Super => modt == ModifierType::SUPER_MASK,
Modifier::None => false,
};
if launch_mod && plugin_keys.contains(&key) {
if let Some(ch) = key.name().unwrap_or_default().to_string().pop() {
event_sender
.send_blocking(TransferType::CloseOverview(
CloseOverviewConfig::LauncherPress(ch),
))
.warn_details("unable to send");
}
return Propagation::Stop;
}
match (launch_mod, key) {
(_, Key::Escape) => {
event_sender
.send_blocking(TransferType::Exit)
.warn_details("unable to send");
Propagation::Stop
}
(_, Key::Tab) => {
event_sender
.send_blocking(TransferType::SwitchOverview(SwitchOverviewConfig {
workspace: false,
direction: Direction::Right,
}))
.warn_details("unable to send");
Propagation::Stop
}
(_, Key::ISO_Left_Tab | Key::grave | Key::dead_grave) => {
event_sender
.send_blocking(TransferType::SwitchOverview(SwitchOverviewConfig {
workspace: false,
direction: Direction::Left,
}))
.warn_details("unable to send");
Propagation::Stop
}
(true, Key::h) => {
event_sender
.send_blocking(TransferType::SwitchOverview(SwitchOverviewConfig {
workspace: true,
direction: Direction::Left,
}))
.warn_details("unable to send");
Propagation::Stop
}
(true, Key::l) => {
event_sender
.send_blocking(TransferType::SwitchOverview(SwitchOverviewConfig {
workspace: true,
direction: Direction::Right,
}))
.warn_details("unable to send");
Propagation::Stop
}
(_, Key::Left) => {
if !entry.text().is_empty() {
return Propagation::Proceed;
}
event_sender
.send_blocking(TransferType::SwitchOverview(SwitchOverviewConfig {
workspace: true,
direction: Direction::Left,
}))
.warn_details("unable to send");
Propagation::Stop
}
(_, Key::Right) => {
if !entry.text().is_empty() {
return Propagation::Proceed;
}
event_sender
.send_blocking(TransferType::SwitchOverview(SwitchOverviewConfig {
workspace: true,
direction: Direction::Right,
}))
.warn_details("unable to send");
Propagation::Stop
}
(_, Key::Up) | (true, Key::k) => {
event_sender
.send_blocking(TransferType::SwitchOverview(SwitchOverviewConfig {
workspace: true,
direction: Direction::Up,
}))
.warn_details("unable to send");
Propagation::Stop
}
(_, Key::Down) | (true, Key::j) => {
event_sender
.send_blocking(TransferType::SwitchOverview(SwitchOverviewConfig {
workspace: true,
direction: Direction::Down,
}))
.warn_details("unable to send");
Propagation::Stop
}
(_, Key::Return) => {
if results.first_child().is_some() {
event_sender
.send_blocking(TransferType::CloseOverview(CloseOverviewConfig::None))
.warn_details("unable to send");
}
Propagation::Stop
}
(true, Key::_1) => {
if results.observe_children().into_iter().len() > 1 {
event_sender
.send_blocking(TransferType::CloseOverview(
CloseOverviewConfig::LauncherPress('1'),
))
.warn_details("unable to send");
}
Propagation::Stop
}
(true, Key::_2) => {
if results.observe_children().into_iter().len() > 2 {
event_sender
.send_blocking(TransferType::CloseOverview(
CloseOverviewConfig::LauncherPress('2'),
))
.warn_details("unable to send");
}
Propagation::Stop
}
(true, Key::_3) => {
if results.observe_children().into_iter().len() > 3 {
event_sender
.send_blocking(TransferType::CloseOverview(
CloseOverviewConfig::LauncherPress('3'),
))
.warn_details("unable to send");
}
Propagation::Stop
}
(true, Key::_4) => {
if results.observe_children().into_iter().len() > 4 {
event_sender
.send_blocking(TransferType::CloseOverview(
CloseOverviewConfig::LauncherPress('4'),
))
.warn_details("unable to send");
}
Propagation::Stop
}
(true, Key::_5) => {
if results.observe_children().into_iter().len() > 5 {
event_sender
.send_blocking(TransferType::CloseOverview(
CloseOverviewConfig::LauncherPress('5'),
))
.warn_details("unable to send");
}
Propagation::Stop
}
(true, Key::_6) => {
if results.observe_children().into_iter().len() > 6 {
event_sender
.send_blocking(TransferType::CloseOverview(
CloseOverviewConfig::LauncherPress('6'),
))
.warn_details("unable to send");
}
Propagation::Stop
}
(true, Key::_7) => {
if results.observe_children().into_iter().len() > 7 {
event_sender
.send_blocking(TransferType::CloseOverview(
CloseOverviewConfig::LauncherPress('7'),
))
.warn_details("unable to send");
}
Propagation::Stop
}
(true, Key::_8) => {
if results.observe_children().into_iter().len() > 8 {
event_sender
.send_blocking(TransferType::CloseOverview(
CloseOverviewConfig::LauncherPress('8'),
))
.warn_details("unable to send");
}
Propagation::Stop
}
(true, Key::_9) => {
if results.observe_children().into_iter().len() > 9 {
event_sender
.send_blocking(TransferType::CloseOverview(
CloseOverviewConfig::LauncherPress('9'),
))
.warn_details("unable to send");
}
Propagation::Stop
}
_ => Propagation::Proceed,
}
}