gpui-component/crates/wef/examples/wef-winit/src/main.rs
2025-06-18 10:46:55 +08:00

364 lines
12 KiB
Rust

use std::{rc::Rc, time::Duration};
use image::{GenericImage, RgbaImage, buffer::ConvertBuffer};
use softbuffer::Surface;
use wef::{
Browser, BrowserHandler, DirtyRects, ImageBuffer, KeyCode, KeyModifier, LogicalUnit,
MouseButton, PaintElementType, Rect, Settings,
};
use winit::{
application::ApplicationHandler,
dpi::LogicalSize,
event::{Ime, Modifiers, MouseScrollDelta, WindowEvent},
event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy},
keyboard::{ModifiersState, NamedKey},
window::{Window, WindowId},
};
type BoxError = Box<dyn std::error::Error>;
enum UserEvent {
WefMessagePump,
Exit,
}
struct State {
scale_factor: f32,
browser: Browser,
}
impl State {
fn new(event_loop_proxy: EventLoopProxy<UserEvent>, window: Window) -> Result<Self, BoxError> {
let window = Rc::new(window);
let inner_size = window.inner_size();
let context = softbuffer::Context::new(window.clone())?;
let mut surface = Surface::new(&context, window.clone())?;
surface.resize(
inner_size.width.try_into().expect("valid surface width"),
inner_size.height.try_into().expect("valid surface height"),
)?;
let scale_factor = window.scale_factor() as f32;
// Create the browser instance
let browser = Browser::builder()
.size(inner_size.width, inner_size.height)
.device_scale_factor(scale_factor)
.url("https://www.google.com")
.handler(MyHandler {
scale_factor,
surface,
window,
view_image: None,
popup_rect: Rect::default(),
popup_image: None,
event_loop_proxy,
})
.build();
browser.set_focus(true);
Ok(Self {
scale_factor,
browser,
})
}
}
struct App {
event_loop_proxy: EventLoopProxy<UserEvent>,
state: Option<State>,
key_modifiers: KeyModifier,
}
impl App {
fn new(event_loop_proxy: EventLoopProxy<UserEvent>) -> Self {
Self {
event_loop_proxy,
state: None,
key_modifiers: KeyModifier::empty(),
}
}
#[inline]
fn browser(&self) -> Option<&Browser> {
self.state.as_ref().map(|state| &state.browser)
}
}
impl ApplicationHandler<UserEvent> for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window = event_loop
.create_window(
Window::default_attributes().with_inner_size(LogicalSize::new(1024, 768)),
)
.unwrap();
window.set_ime_allowed(true);
self.state =
Some(State::new(self.event_loop_proxy.clone(), window).expect("create window"));
}
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
match event {
UserEvent::WefMessagePump => wef::do_message_work(),
UserEvent::Exit => event_loop.exit(),
}
}
fn window_event(
&mut self,
_event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: winit::event::WindowEvent,
) {
match event {
WindowEvent::CloseRequested => {
if let Some(browser) = self.browser() {
browser.close();
}
}
WindowEvent::Resized(size) => {
if let Some(state) = &mut self.state {
state.browser.resize(wef::Size::new(
wef::PhysicalUnit(size.width as i32),
wef::PhysicalUnit(size.height as i32),
));
}
}
WindowEvent::CursorMoved { position, .. } => {
let scale_factor = self.state.as_ref().unwrap().scale_factor;
let position = position.to_logical::<f32>(scale_factor as f64);
if let Some(browser) = self.browser() {
browser.send_mouse_move_event(
wef::Point::new(
wef::LogicalUnit(position.x as i32),
wef::LogicalUnit(position.y as i32),
),
self.key_modifiers,
);
}
}
WindowEvent::MouseInput { state, button, .. } => {
let button = match button {
winit::event::MouseButton::Left => MouseButton::Left,
winit::event::MouseButton::Middle => MouseButton::Middle,
winit::event::MouseButton::Right => MouseButton::Right,
_ => return,
};
let pressed = state.is_pressed();
if let Some(browser) = self.browser() {
browser.send_mouse_click_event(button, !pressed, 1, self.key_modifiers);
}
}
WindowEvent::MouseWheel { delta, .. } => {
let (delta_x, delta_y) = match delta {
MouseScrollDelta::LineDelta(x, y) => (50 * x as i32, 50 * y as i32),
MouseScrollDelta::PixelDelta(delta) => (delta.x as _, delta.y as _),
};
if let Some(browser) = self.browser() {
browser.send_mouse_wheel_event(wef::Point::new(
wef::LogicalUnit(delta_x),
wef::LogicalUnit(delta_y),
));
}
}
WindowEvent::ModifiersChanged(modifiers) => {
self.key_modifiers = convert_key_modifiers(modifiers);
}
WindowEvent::KeyboardInput { event, .. } => match event.logical_key.as_ref() {
winit::keyboard::Key::Named(named_key) => {
if let Some(key_code) = convert_key_code(named_key) {
if let Some(browser) = self.browser() {
browser.send_key_event(
event.state.is_pressed(),
key_code,
self.key_modifiers,
);
}
}
}
winit::keyboard::Key::Character(s) if event.state.is_pressed() => {
if let Some(browser) = self.browser() {
for ch in s.chars() {
browser.send_char_event(ch as u16);
}
}
}
_ => {}
},
WindowEvent::Ime(ime) => match ime {
Ime::Preedit(text, range) => {
let (start, end) = range.unwrap_or_default();
if let Some(browser) = self.browser() {
browser.ime_set_composition(&text, start, end);
}
}
Ime::Commit(text) => {
if let Some(browser) = self.browser() {
browser.ime_commit(&text)
}
}
_ => {}
},
WindowEvent::Focused(focused) => {
if let Some(browser) = self.browser() {
browser.set_focus(focused);
}
}
_ => (),
}
}
}
struct MyHandler {
scale_factor: f32,
surface: Surface<Rc<Window>, Rc<Window>>,
window: Rc<Window>,
view_image: Option<RgbaImage>,
popup_rect: Rect<LogicalUnit<i32>>,
popup_image: Option<RgbaImage>,
event_loop_proxy: EventLoopProxy<UserEvent>,
}
impl BrowserHandler for MyHandler {
fn on_closed(&mut self) {
_ = self.event_loop_proxy.send_event(UserEvent::Exit);
}
fn on_paint(
&mut self,
type_: PaintElementType,
_dirty_rects: &DirtyRects,
image_buffer: ImageBuffer,
) {
match type_ {
PaintElementType::View => match &mut self.view_image {
Some(view_image)
if view_image.width() == image_buffer.width()
&& view_image.height() == image_buffer.height() =>
{
view_image.copy_from_slice(&image_buffer);
}
_ => self.view_image = Some(image_buffer.convert()),
},
PaintElementType::Popup => {
self.popup_image = Some(image_buffer.convert());
}
}
if let Some(view_image) = &self.view_image {
self.surface
.resize(
view_image.width().try_into().expect("valid surface width"),
view_image
.height()
.try_into()
.expect("valid surface height"),
)
.expect("resize surface");
let mut buffer = self.surface.buffer_mut().unwrap();
let mut dest = image::ImageBuffer::<image::Rgba<u8>, &mut [u8]>::from_raw(
view_image.width(),
view_image.height(),
unsafe {
std::slice::from_raw_parts_mut(
buffer.as_mut_ptr() as *mut u8,
(view_image.width() * view_image.height() * 4) as usize,
)
},
)
.unwrap();
dest.copy_from_slice(view_image);
if let Some(popup_image) = &self.popup_image {
let origin = self
.popup_rect
.origin()
.map(|x| x.to_physical(self.scale_factor));
dest.copy_from(popup_image, origin.x.0 as u32, origin.y.0 as u32)
.unwrap();
}
buffer.present().unwrap();
}
}
fn on_title_changed(&mut self, title: &str) {
self.window.set_title(title);
}
fn on_popup_show(&mut self, show: bool) {
if !show {
self.popup_image = None;
}
}
fn on_popup_position(&mut self, rect: Rect<LogicalUnit<i32>>) {
self.popup_rect = rect;
}
}
fn convert_key_code(key: NamedKey) -> Option<KeyCode> {
match key {
NamedKey::Backspace => Some(KeyCode::Backspace),
NamedKey::Delete => Some(KeyCode::Delete),
NamedKey::Tab => Some(KeyCode::Tab),
NamedKey::Enter => Some(KeyCode::Enter),
NamedKey::PageUp => Some(KeyCode::PageUp),
NamedKey::PageDown => Some(KeyCode::PageDown),
NamedKey::End => Some(KeyCode::End),
NamedKey::Home => Some(KeyCode::Home),
NamedKey::ArrowLeft => Some(KeyCode::ArrowLeft),
NamedKey::ArrowUp => Some(KeyCode::ArrowUp),
NamedKey::ArrowRight => Some(KeyCode::ArrowRight),
NamedKey::ArrowDown => Some(KeyCode::ArrowDown),
_ => None,
}
}
fn convert_key_modifiers(modifiers: Modifiers) -> KeyModifier {
let mut key_modifiers = KeyModifier::empty();
if modifiers.state().contains(ModifiersState::SHIFT) {
key_modifiers |= KeyModifier::SHIFT;
}
if modifiers.state().contains(ModifiersState::CONTROL) {
key_modifiers |= KeyModifier::CONTROL;
}
if modifiers.state().contains(ModifiersState::ALT) {
key_modifiers |= KeyModifier::ALT;
}
key_modifiers
}
fn run() -> Result<(), Box<dyn std::error::Error>> {
let event_loop = EventLoop::<UserEvent>::with_user_event().build()?;
let event_loop_proxy = event_loop.create_proxy();
if cfg!(target_os = "linux") {
std::thread::spawn({
let event_loop_proxy = event_loop_proxy.clone();
move || {
loop {
std::thread::sleep(Duration::from_millis(1000 / 60));
if event_loop_proxy
.send_event(UserEvent::WefMessagePump)
.is_err()
{
break;
}
}
}
});
}
let mut app = App::new(event_loop_proxy);
event_loop.run_app(&mut app)?;
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
wef::launch(Settings::new(), run)?;
Ok(())
}