如何在Rust中创建一个可以点击的覆盖层,但仍然捕获输入?

huangapple go评论72阅读模式
英文:

How can I make a click-through overlay in Rust that still captures input?

问题

以下是您提供的代码的翻译部分,代码本身不需要翻译:

我已经有创建透明窗口并在其上绘制正方形的代码,使用[winit](https://crates.io/crates/winit/0.26.1/dependencies)和[pixels](https://crates.io/crates/pixels)。但我无法使其透明,也就是说,让用户与叠加窗口后面的内容进行交互,同时仍然允许应用程序捕获输入。以下是我的代码的最小示例:

fn main() {
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new()
        .with_fullscreen(Some(winit::window::Fullscreen::Borderless(None)))
        .with_transparent(true)
        .build(&event_loop)
        .unwrap();

    window.set_window_level(WindowLevel::AlwaysOnTop);
    window.set_cursor_hittest(false).unwrap();

    let window_size = window.inner_size();
    let surface = SurfaceTexture::new(window_size.width, window_size.height, &window);

    let mut pixels = Pixels::new(window_size.width, window_size.height, surface).unwrap();
    pixels.set_clear_color(Color::TRANSPARENT);

    event_loop.run(move |event, _, control_flow| {
        control_flow.set_wait();

        match event {
            Event::WindowEvent {
                event: window_event,
                ..
            } => match window_event {
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            virtual_keycode: Some(VirtualKeyCode::Space),
                            state: ElementState::Pressed,
                            ..
                        },
                    ..
                } => {
                    println!("Input from window event");
                }

                WindowEvent::CloseRequested => control_flow.set_exit(),

                _ => (),
            },

            Event::DeviceEvent {
                event:
                    DeviceEvent::Key(KeyboardInput {
                        virtual_keycode: Some(VirtualKeyCode::Space),
                        state: ElementState::Pressed,
                        ..
                    }),
                ..
            } => {
                println!("Input from device event");
            }

            Event::RedrawRequested(_) => {
                pixels.render().unwrap();
            }

            _ => (),
        }
    });
}

希望这对您有所帮助。如果您有其他问题或需要进一步帮助,请告诉我。

英文:

I already have code that creates a transparent window and draws a square on it using winit and pixels, but I can't make it click-through, that is, let the user interact with what is behind the overlay window, while still letting the app capture input. Here's a minimal example of my code:

use pixels::{wgpu::Color, Pixels, SurfaceTexture};
use winit::{
    event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
    event_loop::EventLoop,
    platform::windows::{WindowExtWindows, HWND},
    window::{WindowBuilder, WindowLevel},
};

fn main() {
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new()
        .with_fullscreen(Some(winit::window::Fullscreen::Borderless(None)))
        .with_transparent(true)
        .build(&event_loop)
        .unwrap();

    window.set_window_level(WindowLevel::AlwaysOnTop);
    window.set_cursor_hittest(false).unwrap();

    let window_size = window.inner_size();
    let surface = SurfaceTexture::new(window_size.width, window_size.height, &window);

    let mut pixels = Pixels::new(window_size.width, window_size.height, surface).unwrap();
    pixels.set_clear_color(Color::TRANSPARENT);

    event_loop.run(move |event, _, control_flow| {
        control_flow.set_wait();

        match event {
            Event::WindowEvent {
                event: window_event,
                ..
            } => match window_event {
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            virtual_keycode: Some(VirtualKeyCode::Space),
                            state: ElementState::Pressed,
                            ..
                        },
                    ..
                } => {
                    println!("Input from window event");
                }

                WindowEvent::CloseRequested => control_flow.set_exit(),

                _ => (),
            },

            Event::DeviceEvent {
                event:
                    DeviceEvent::Key(KeyboardInput {
                        virtual_keycode: Some(VirtualKeyCode::Space),
                        state: ElementState::Pressed,
                        ..
                    }),
                ..
            } => {
                println!("Input from device event");
            }

            Event::RedrawRequested(_) => {
                pixels.render().unwrap();
            }

            _ => (),
        }
    });
}

I thought that Event::DeviceEvent would work because it seemed like it wasn't restricted to a specific window, but it is. In every scenario I've tried, both or none of the println!()s were called. Do I need another crate for that?

答案1

得分: 0

"device_query" crate可以解决这个问题。它甚至不需要一个窗口来运行,因为输入是通过在键盘和鼠标的DeviceState实例上调用DeviceState::get_keys()DeviceState::mouse()来按需查询的。

use device_query::{DeviceState, DeviceQuery};

// 廉价创建一个空的DeviceState
let device_state = DeviceState::new();

// 这些方法查询输入。它们是分别惰性查询的。
let keys = device_state.get_keys();
let mouse = device_state.get_mouse();

let is_alt_pressed = keys.contains(&Keycode::LAlt);
let is_m1_pressed = mouse.button_pressed[1]; // 它从[1]开始表示M1。[0]没有意义。

上述代码可以在没有窗口焦点甚至没有窗口的情况下捕获输入。在问题中提供的代码中,它应该放在MainEventsCleared事件内。还需要将event_loop.run()闭包内的第一行的control_flow.set_wait()替换为control_flow.set_poll(),以便MainEventsCleared将始终运行,即使没有新的事件。

英文:

The crate device_query can solve the problem. It doesn't even need a window to function, as the input is queried on demand by calling DeviceState::get_keys() and DeviceState::mouse() on a DeviceState instance for keyboard and mouse respectively.

use device_query::{DeviceState, DeviceQuery};

// Cheaply creates an empty DeviceState
let device_state = DeviceState::new();

// Those methods query the input. They are individualy lazily queried.
let keys = device_state.get_keys();
let mouse = device_state.get_mouse();

let is_alt_pressed = keys.contains(&Keycode::LAlt);
let is_m1_pressed = mouse.button_pressed[1]; // It starts at [1] for M1. [0] has no meaning.

The above code can capture input without window focus and even without a window at all. In the code provided in the question, it should be put inside the MainEventsCleared event. It is also required to replace control_flow.set_wait() in the first line inside the event_loop.run() closure with control_flow.set_poll(), so that MainEventsCleared will always run, even without new events.

答案2

得分: 0

window.set_cursor_hittest(false) 可以工作,尽管这仅在某些平台上受支持。

英文:

It looks like window.set_cursor_hittest(false) will work although this is only supported on certain platforms.

huangapple
  • 本文由 发表于 2023年3月4日 02:44:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/75630785.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定