tauri-apps / global-hotkey

Global hotkeys for Desktop Applications.
Apache License 2.0
157 stars 18 forks source link

Keybinds not detected for Iced code. Only works for 0.2.4 and not the latest (0.5.1) #68

Closed nednoodlehead closed 7 months ago

nednoodlehead commented 7 months ago

I was hoping to make a pr for an iced example, and I have a working example for version 0.2.4, but when I try to upgrade it into the latest version, it fails to detect inputs. The change log between these versions appears to contain nothing that would affect this.

So below is the working 0.2.4 example:

// 0.2.4 imports
use global_hotkey::hotkey::{Code, HotKey, Modifiers};
use global_hotkey::{GlobalHotKeyEvent, GlobalHotKeyManager};

use iced::futures::SinkExt;
use iced::widget::{container, row, text};
use iced::{executor, Application, Command, Element, Subscription, Theme};

struct Example {
    last_pressed: String,
}

#[derive(Debug, Clone)]
enum ProgramCommands {
    Received(String),
}

impl Application for Example {
    type Executor = executor::Default;
    type Message = ProgramCommands;
    type Theme = Theme;
    type Flags = ();

    fn new(_flags: Self::Flags) -> (Example, iced::Command<Self::Message>) {
        // you can also put the manager inside of the `Example` struct, and create an interface to rebind
        let manager = GlobalHotKeyManager::new().unwrap();
        let hotkey_1 = HotKey::new(Some(Modifiers::CONTROL), Code::ArrowRight);
        let hotkey_2 = HotKey::new(None, Code::ArrowUp);
        manager.register(hotkey_2).unwrap();
        manager.register(hotkey_1).unwrap();
        (
            Example {
                last_pressed: "".to_string(),
            },
            Command::none(),
        )
    }
    fn title(&self) -> String {
        String::from("Iced example!")
    }

    fn theme(&self) -> Self::Theme {
        Theme::Dark // dark theme :D
    }
    fn update(&mut self, msg: Self::Message) -> iced::Command<ProgramCommands> {
        match msg {
            Self::Message::Received(code) => {
                self.last_pressed = code.to_string();

                Command::none()
            }
        }
    }
    fn view(&self) -> Element<'_, Self::Message> {
        container(row![text("You pressed: "), text(self.last_pressed.clone())]).into()
    }

    fn subscription(&self) -> Subscription<Self::Message> {
        // if you want to have multiple subscriptions, do it like this:
        // iced::Subscription::batch(vec![self.hotkey_sub(), self.test_sub()])
        self.hotkey_sub()
    }
}

fn main() -> iced::Result {
    Example::run(iced::Settings::default())
}

impl Example {
    pub fn hotkey_sub(&self) -> Subscription<ProgramCommands> {
        iced::subscription::channel(4, 32, |mut sender| async move {
            let receiver = GlobalHotKeyEvent::receiver();
            loop {
                match receiver.try_recv() {
                    Err(_e) => {
                        // nothing, this happens when nothing happens
                    }
                    Ok(t) => {
                        // if you want to differenciate between the different codes, you can match them:
                        // match t {
                        //     GlobalHotKeyEvent { id: 267073199 } => { // this is for up arrow, no modifiers
                        //         do whatever
                        //     }
                        // }
                        sender
                            .send(ProgramCommands::Received(format!("{:?}", t)))
                            .await
                            .unwrap()
                    }
                }
                // so this will occur infinitely, i would really recommend using the `async_std` crate to put a sleep here
                async_std::task::sleep(std::time::Duration::from_millis(50)).await;
            }
        })
    }
}

(Requires iced = "0.12.1" & async-std = "1.12")

nednoodlehead commented 7 months ago

It seems that the 0.4.0 update broke functionality for my example, adding the pressed and release states.

Not really sure why, it seems like that feature is independent to how events are received...

amrbashir commented 7 months ago

This is because you don't store GlobalHotkeyManager and its Drop implementation gets called before your app even starts so you just need to store it inside Example struct and it should work fine.