hyprswitch 3.3.2

A CLI/GUI that allows switching between windows in Hyprland
use crate::cli::shared;
use crate::cli::shared::InputReverseKey;
use crate::handle::get_monitors;
use crate::{GuiConfig, SubmapConfig};
use clap::{Args, ValueEnum};
use hyprswitch::CloseType;
use serde::{Deserialize, Serialize};
use std::str::FromStr;

#[derive(Args, Debug, Clone)]
pub struct SubmapInfo {
    /// The name of the submap to activate (generated by hyprswitch generate) [conflicts with --mod_key, --key, --close]
    #[arg(long, conflicts_with_all = ["mod_key", "key", "close"])]
    pub submap: String,
}

#[derive(Args, Debug, Clone)]
pub struct SubmapConf {
    /// The modifier key to used to open the GUI (e.g. shift, alt, ...)
    #[clap(long, value_enum)]
    pub mod_key: shared::InputModKey,

    /// The key to used to open the GUI (e.g., tab, grave, ...)
    #[arg(long)]
    pub key: String,

    /// How to close hyprswitch
    #[clap(long, default_value_t, value_enum)]
    pub close: InputCloseType,
}

#[derive(Args, Debug, Clone)]
pub struct GuiConf {
    /// The maximum offset you can switch to with number keys and is shown in the GUI (pass 0 to disable the number keys and index)
    #[arg(long, default_value = "6", value_parser = clap::value_parser!(u8).range(0..=9))]
    pub max_switch_offset: u8,

    /// Hide the active window border in the GUI (also hides the border for selected workspace or monitor)
    #[arg(long, default_value = "false", action = clap::ArgAction::Set, default_missing_value = "true", num_args= 0..=1
    )]
    pub hide_active_window_border: bool,

    /// Show the GUI only on this monitor(s) [default: display on all monitors]
    ///
    /// Example: `--monitors=HDMI-0,DP-1` / `--monitors=eDP-1`
    ///
    /// Available values: `hyprctl monitors -j | jq '.[].name'`
    #[arg(long, value_delimiter = ',', value_parser = clap::value_parser!(Monitors))]
    pub monitors: Option<Monitors>,

    /// Show all workspaces on all monitors [default: only show workspaces on the corresponding monitor]
    #[arg(long, default_value = "false", action = clap::ArgAction::Set, default_missing_value = "true", num_args=0..=1
    )]
    pub show_workspaces_on_all_monitors: bool,

    /// Show the application launcher in the GUI
    #[arg(long, default_value = "false", action = clap::ArgAction::Set, default_missing_value = "true", num_args=0..=1
    )]
    pub show_launcher: bool,
}

impl From<GuiConf> for GuiConfig {
    fn from(opts: GuiConf) -> Self {
        Self {
            max_switch_offset: opts.max_switch_offset,
            hide_active_window_border: opts.hide_active_window_border,
            monitors: opts.monitors.map(|m| m.0),
            show_workspaces_on_all_monitors: opts.show_workspaces_on_all_monitors,
            show_launcher: opts.show_launcher,
        }
    }
}

impl SubmapInfo {
    #[allow(dead_code)]
    pub fn into_submap_info(self, reverse_key: InputReverseKey) -> SubmapConfig {
        SubmapConfig::Name {
            name: self.submap,
            reverse_key: reverse_key.into(),
        }
    }
}

impl SubmapConf {
    pub fn into_submap_conf(self, reverse_key: InputReverseKey) -> SubmapConfig {
        SubmapConfig::Config {
            mod_key: self.mod_key.into(),
            key: self.key,
            close: self.close.into(),
            reverse_key: reverse_key.into(),
        }
    }
}

#[derive(ValueEnum, Clone, Default, Debug, Serialize, Deserialize, PartialEq)]
pub enum InputCloseType {
    #[default]
    /// Close when *pressing enter* or an index key (1, 2, 3, ...) or clicking on a window in GUI (or pressing escape)
    Default,
    /// Close when *releasing the mod key* (e.g., SUPER) or clicking on a window in GUI (or pressing escape)
    ModKeyRelease,
}

impl From<InputCloseType> for CloseType {
    fn from(s: InputCloseType) -> Self {
        match s {
            InputCloseType::Default => Self::Default,
            InputCloseType::ModKeyRelease => Self::ModKeyRelease,
        }
    }
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Monitors(pub Vec<String>);

impl FromStr for Monitors {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let available: Vec<hyprland::data::Monitor> = get_monitors();
        let mut vec = Vec::new();
        for monitor in s.split(',') {
            if let Some(m) = available.iter().find(|m| m.name == monitor) {
                vec.push(m.name.clone())
            } else {
                return Err(format!(
                    "{s} not found in {:?}",
                    available.iter().map(|a| a.name.clone()).collect::<Vec<_>>()
                ));
            }
        }
        Ok(Self(vec))
    }
}