winit-core/ime: add more purposes and content hints
Also move IME example into `ime.rs` thus making IME integrations more easily comprehendible. Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
parent
014fb68a26
commit
6de5041a94
20 changed files with 554 additions and 182 deletions
|
|
@ -1 +0,0 @@
|
|||
../examples
|
||||
1301
winit/examples/application.rs
Normal file
1301
winit/examples/application.rs
Normal file
File diff suppressed because it is too large
Load diff
129
winit/examples/child_window.rs
Normal file
129
winit/examples/child_window.rs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#[cfg(any(x11_platform, macos_platform, windows_platform))]
|
||||
#[allow(deprecated)]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalPosition, LogicalSize, Position};
|
||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::raw_window_handle::HasRawWindowHandle;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WindowData {
|
||||
window: Box<dyn Window>,
|
||||
color: u32,
|
||||
}
|
||||
|
||||
impl WindowData {
|
||||
fn new(window: Box<dyn Window>, color: u32) -> Self {
|
||||
Self { window, color }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct Application {
|
||||
parent_window_id: Option<WindowId>,
|
||||
windows: HashMap<WindowId, WindowData>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for Application {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let attributes = WindowAttributes::default()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
|
||||
let window = event_loop.create_window(attributes).unwrap();
|
||||
println!("Parent window id: {:?})", window.id());
|
||||
self.parent_window_id = Some(window.id());
|
||||
|
||||
self.windows.insert(window.id(), WindowData::new(window, 0xffbbbbbb));
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
self.windows.clear();
|
||||
event_loop.exit();
|
||||
},
|
||||
WindowEvent::PointerEntered { device_id: _, .. } => {
|
||||
// On x11, println when the cursor entered in a window even if the child window
|
||||
// is created by some key inputs.
|
||||
// the child windows are always placed at (0, 0) with size (200, 200) in the
|
||||
// parent window, so we also can see this log when we move
|
||||
// the cursor around (200, 200) in parent window.
|
||||
println!("cursor entered in the window {window_id:?}");
|
||||
},
|
||||
WindowEvent::KeyboardInput {
|
||||
event: KeyEvent { state: ElementState::Pressed, .. },
|
||||
..
|
||||
} => {
|
||||
let child_index = self.windows.len() - 1;
|
||||
let child_color =
|
||||
0xff000000 + 3_u32.pow((child_index + 2).rem_euclid(16) as u32);
|
||||
|
||||
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap();
|
||||
let child_window =
|
||||
spawn_child_window(parent_window.window.as_ref(), event_loop, child_index);
|
||||
let child_id = child_window.id();
|
||||
println!("Child window created with id: {child_id:?}");
|
||||
self.windows.insert(child_id, WindowData::new(child_window, child_color));
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = self.windows.get(&window_id) {
|
||||
if window_id == self.parent_window_id.unwrap() {
|
||||
fill::fill_window(window.window.as_ref());
|
||||
} else {
|
||||
fill::fill_window_with_color(window.window.as_ref(), window.color);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_child_window(
|
||||
parent: &dyn Window,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
child_count: usize,
|
||||
) -> Box<dyn Window> {
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
|
||||
// As child count increases, x goes from 0*128 to 5*128 and then repeats
|
||||
let x: f64 = child_count.rem_euclid(5) as f64 * 128.0;
|
||||
|
||||
// After 5 windows have been put side by side horizontally, a new row starts
|
||||
let y: f64 = (child_count / 5) as f64 * 96.0;
|
||||
|
||||
let mut window_attributes = WindowAttributes::default()
|
||||
.with_title("child window")
|
||||
.with_surface_size(LogicalSize::new(128.0f32, 96.0))
|
||||
.with_position(Position::Logical(LogicalPosition::new(x, y)))
|
||||
.with_visible(true);
|
||||
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
||||
window_attributes = unsafe { window_attributes.with_parent_window(Some(parent)) };
|
||||
|
||||
event_loop.create_window(window_attributes).unwrap()
|
||||
}
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
event_loop.run_app(Application::default())
|
||||
}
|
||||
|
||||
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))]
|
||||
fn main() {
|
||||
panic!(
|
||||
"This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature \
|
||||
enabled."
|
||||
);
|
||||
}
|
||||
146
winit/examples/control_flow.rs
Normal file
146
winit/examples/control_flow.rs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::thread;
|
||||
#[cfg(not(web_platform))]
|
||||
use std::time;
|
||||
|
||||
use ::tracing::{info, warn};
|
||||
#[cfg(web_platform)]
|
||||
use web_time as time;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
|
||||
use winit::keyboard::{Key, NamedKey};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
#[path = "util/tracing.rs"]
|
||||
mod tracing;
|
||||
|
||||
const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
|
||||
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Mode {
|
||||
#[default]
|
||||
Wait,
|
||||
WaitUntil,
|
||||
Poll,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
#[cfg(web_platform)]
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
tracing::init();
|
||||
|
||||
info!("Press '1' to switch to Wait mode.");
|
||||
info!("Press '2' to switch to WaitUntil mode.");
|
||||
info!("Press '3' to switch to Poll mode.");
|
||||
info!("Press 'R' to toggle request_redraw() calls.");
|
||||
info!("Press 'Esc' to close the window.");
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
event_loop.run_app(ControlFlowDemo::default())
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct ControlFlowDemo {
|
||||
mode: Mode,
|
||||
request_redraw: bool,
|
||||
wait_cancelled: bool,
|
||||
close_requested: bool,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for ControlFlowDemo {
|
||||
fn new_events(&mut self, _event_loop: &dyn ActiveEventLoop, cause: StartCause) {
|
||||
info!("new_events: {cause:?}");
|
||||
|
||||
self.wait_cancelled = match cause {
|
||||
StartCause::WaitCancelled { .. } => self.mode == Mode::WaitUntil,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default().with_title(
|
||||
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
|
||||
);
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
_event_loop: &dyn ActiveEventLoop,
|
||||
_window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
info!("{event:?}");
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
self.close_requested = true;
|
||||
},
|
||||
WindowEvent::KeyboardInput {
|
||||
event: KeyEvent { logical_key: key, state: ElementState::Pressed, .. },
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
// WARNING: Consider using `key_without_modifiers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
Key::Character("1") => {
|
||||
self.mode = Mode::Wait;
|
||||
warn!("mode: {:?}", self.mode);
|
||||
},
|
||||
Key::Character("2") => {
|
||||
self.mode = Mode::WaitUntil;
|
||||
warn!("mode: {:?}", self.mode);
|
||||
},
|
||||
Key::Character("3") => {
|
||||
self.mode = Mode::Poll;
|
||||
warn!("mode: {:?}", self.mode);
|
||||
},
|
||||
Key::Character("r") => {
|
||||
self.request_redraw = !self.request_redraw;
|
||||
warn!("request_redraw: {}", self.request_redraw);
|
||||
},
|
||||
Key::Named(NamedKey::Escape) => {
|
||||
self.close_requested = true;
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
let window = self.window.as_ref().unwrap();
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
if self.request_redraw && !self.wait_cancelled && !self.close_requested {
|
||||
self.window.as_ref().unwrap().request_redraw();
|
||||
}
|
||||
|
||||
match self.mode {
|
||||
Mode::Wait => event_loop.set_control_flow(ControlFlow::Wait),
|
||||
Mode::WaitUntil => {
|
||||
if !self.wait_cancelled {
|
||||
event_loop
|
||||
.set_control_flow(ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME));
|
||||
}
|
||||
},
|
||||
Mode::Poll => {
|
||||
thread::sleep(POLL_SLEEP_TIME);
|
||||
event_loop.set_control_flow(ControlFlow::Poll);
|
||||
},
|
||||
};
|
||||
|
||||
if self.close_requested {
|
||||
event_loop.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
winit/examples/data/cross.png
Normal file
BIN
winit/examples/data/cross.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 B |
BIN
winit/examples/data/cross2.png
Normal file
BIN
winit/examples/data/cross2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 129 B |
BIN
winit/examples/data/gradient.png
Normal file
BIN
winit/examples/data/gradient.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 229 B |
BIN
winit/examples/data/icon.png
Normal file
BIN
winit/examples/data/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
65
winit/examples/dnd.rs
Normal file
65
winit/examples/dnd.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use std::error::Error;
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
#[path = "util/tracing.rs"]
|
||||
mod tracing;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
tracing::init();
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
|
||||
let app = Application::new();
|
||||
Ok(event_loop.run_app(app)?)
|
||||
}
|
||||
|
||||
/// Application state and event handling.
|
||||
#[derive(Debug)]
|
||||
struct Application {
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
fn new() -> Self {
|
||||
Self { window: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler for Application {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes =
|
||||
WindowAttributes::default().with_title("Drag and drop files on me!");
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
_window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
match event {
|
||||
WindowEvent::DragLeft { .. }
|
||||
| WindowEvent::DragEntered { .. }
|
||||
| WindowEvent::DragMoved { .. }
|
||||
| WindowEvent::DragDropped { .. } => {
|
||||
println!("{event:?}");
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
let window = self.window.as_ref().unwrap();
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
WindowEvent::CloseRequested => {
|
||||
event_loop.exit();
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
374
winit/examples/ime.rs
Normal file
374
winit/examples/ime.rs
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
//! Showcases the use of an input method engine (IME)
|
||||
//! by emulating a text edit field.
|
||||
//!
|
||||
//! Use CTRL+i to toggle IME support.
|
||||
//! Use CTRL+p to cycle content purpose values.
|
||||
//! Use CTRL+h to cycle content hint permutations.
|
||||
|
||||
use std::cmp;
|
||||
use std::error::Error;
|
||||
|
||||
use dpi::{LogicalPosition, PhysicalSize};
|
||||
use tracing::{error, info};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{Ime, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::keyboard::{Key, ModifiersState, NamedKey};
|
||||
#[cfg(web_platform)]
|
||||
use winit::platform::web::WindowAttributesWeb;
|
||||
use winit::window::{
|
||||
ImeCapabilities, ImeEnableRequest, ImeHint, ImePurpose, ImeRequest, ImeRequestData,
|
||||
ImeSurroundingText, Window, WindowAttributes, WindowId,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
#[path = "util/tracing.rs"]
|
||||
mod tracing_init;
|
||||
|
||||
const IME_CURSOR_SIZE: PhysicalSize<u32> = PhysicalSize::new(20, 20);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct App {
|
||||
window: Option<Box<dyn Window>>,
|
||||
input_state: TextInputState,
|
||||
modifiers: ModifiersState,
|
||||
}
|
||||
|
||||
/// State of the undisplayed text input field.
|
||||
#[derive(Debug)]
|
||||
struct TextInputState {
|
||||
ime_enabled: bool,
|
||||
/// The contents of the emulated text field for IME purposes (not displayed).
|
||||
/// (text, cursor position in bytes).
|
||||
contents: String,
|
||||
/// The purpose of the contents the emulated text field expects
|
||||
purpose: ImePurpose,
|
||||
/// The behaviour hints for the IME regarding the emulated text field
|
||||
hint: ImeHint,
|
||||
}
|
||||
|
||||
impl TextInputState {
|
||||
fn text_and_cursor(&self) -> (&str, usize) {
|
||||
(&self.contents, self.contents.len())
|
||||
}
|
||||
|
||||
fn append_text(&mut self, text: &str) {
|
||||
self.contents.push_str(text);
|
||||
}
|
||||
|
||||
fn set_text(&mut self, text: String) {
|
||||
self.contents = text;
|
||||
}
|
||||
|
||||
fn pop(&mut self) {
|
||||
self.contents.pop();
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
#[cfg(not(web_platform))]
|
||||
let window_attributes = WindowAttributes::default();
|
||||
#[cfg(web_platform)]
|
||||
let window_attributes = WindowAttributes::default()
|
||||
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
|
||||
self.window = match event_loop.create_window(window_attributes) {
|
||||
Ok(window) => Some(window),
|
||||
Err(err) => {
|
||||
eprintln!("error creating window: {err}");
|
||||
event_loop.exit();
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// Allow IME out of the box.
|
||||
let enable_request = ImeEnableRequest::new(
|
||||
ImeCapabilities::new()
|
||||
.with_hint_and_purpose()
|
||||
.with_cursor_area()
|
||||
.with_surrounding_text(),
|
||||
self.get_ime_update(),
|
||||
)
|
||||
.unwrap();
|
||||
let enable_ime = ImeRequest::Enable(enable_request);
|
||||
|
||||
// Initial update
|
||||
self.window().request_ime_update(enable_ime).unwrap();
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
info!("Close was requested; stopping");
|
||||
self.window = None;
|
||||
event_loop.exit();
|
||||
},
|
||||
WindowEvent::SurfaceResized(_) => {
|
||||
self.window.as_ref().expect("resize event without a window").request_redraw();
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
// Redraw the application.
|
||||
//
|
||||
// It's preferable for applications that do not render continuously to render in
|
||||
// this event rather than in AboutToWait, since rendering in here allows
|
||||
// the program to gracefully handle redraws requested by the OS.
|
||||
|
||||
let window = self.window.as_ref().expect("redraw request without a window");
|
||||
|
||||
// Notify that you're about to draw.
|
||||
window.pre_present_notify();
|
||||
|
||||
// Draw.
|
||||
fill::fill_window(window.as_ref());
|
||||
|
||||
// For contiguous redraw loop you can request a redraw from here.
|
||||
// window.request_redraw();
|
||||
},
|
||||
WindowEvent::Ime(event) => {
|
||||
self.handle_ime_event(event);
|
||||
},
|
||||
WindowEvent::ModifiersChanged(modifiers) => {
|
||||
self.modifiers = modifiers.state();
|
||||
info!("Modifiers changed to {:?}", self.modifiers);
|
||||
},
|
||||
WindowEvent::KeyboardInput { event, is_synthetic: false, .. } => {
|
||||
let mods = self.modifiers;
|
||||
|
||||
// Dispatch actions only on press.
|
||||
if event.state.is_pressed() {
|
||||
self.handle_key_pressed(event, mods);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn handle_key_pressed(&mut self, event: winit::event::KeyEvent, mods: ModifiersState) {
|
||||
match event.key_without_modifiers.as_ref() {
|
||||
Key::Character("i") if mods == ModifiersState::CONTROL => self.toggle_ime(),
|
||||
Key::Character("p") if mods == ModifiersState::CONTROL => {
|
||||
self.input_state.purpose = match self.input_state.purpose {
|
||||
ImePurpose::Normal => ImePurpose::Password,
|
||||
ImePurpose::Password => ImePurpose::Terminal,
|
||||
ImePurpose::Terminal => ImePurpose::Number,
|
||||
ImePurpose::Number => ImePurpose::Phone,
|
||||
ImePurpose::Phone => ImePurpose::Url,
|
||||
ImePurpose::Url => ImePurpose::Email,
|
||||
ImePurpose::Email => ImePurpose::Pin,
|
||||
ImePurpose::Pin => ImePurpose::Date,
|
||||
ImePurpose::Date => ImePurpose::Time,
|
||||
ImePurpose::Time => ImePurpose::DateTime,
|
||||
ImePurpose::DateTime => ImePurpose::Normal,
|
||||
_ => ImePurpose::Normal,
|
||||
};
|
||||
if self.input_state.ime_enabled {
|
||||
self.window()
|
||||
.request_ime_update(ImeRequest::Update(self.get_ime_update()))
|
||||
.unwrap();
|
||||
}
|
||||
info!("text input purpose now {:?}", self.input_state.purpose);
|
||||
},
|
||||
Key::Character("h") if mods == ModifiersState::CONTROL => {
|
||||
let bump = |hint: ImeHint| {
|
||||
if hint.is_all() {
|
||||
ImeHint::NONE
|
||||
} else {
|
||||
// Go through all integers. We'll skip invalid ones
|
||||
ImeHint::from_bits_retain(hint.bits().wrapping_add(1))
|
||||
}
|
||||
};
|
||||
let mut new_hint = bump(self.input_state.hint);
|
||||
|
||||
while !ImeHint::all().contains(new_hint) {
|
||||
new_hint = bump(new_hint);
|
||||
}
|
||||
|
||||
self.input_state.hint = new_hint;
|
||||
|
||||
if self.input_state.ime_enabled {
|
||||
self.window()
|
||||
.request_ime_update(ImeRequest::Update(self.get_ime_update()))
|
||||
.unwrap();
|
||||
}
|
||||
info!("text input IME hint now {:?}", self.input_state.hint);
|
||||
},
|
||||
Key::Named(NamedKey::Backspace) => {
|
||||
self.input_state.pop();
|
||||
self.print_input_state();
|
||||
},
|
||||
_ => {
|
||||
if let Some(text) = event.text_with_all_modifiers {
|
||||
self.input_state.append_text(&text);
|
||||
if self.input_state.ime_enabled {
|
||||
self.window()
|
||||
.request_ime_update(ImeRequest::Update(self.get_ime_update()))
|
||||
.unwrap();
|
||||
}
|
||||
self.print_input_state();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_ime_event(&mut self, event: Ime) {
|
||||
let window = self.window.as_ref().expect("IME request without a window");
|
||||
match event {
|
||||
Ime::Enabled => info!("IME enabled for Window={:?}", window.id()),
|
||||
Ime::Preedit(text, caret_pos) => info!("Preedit: {text}, with caret at {caret_pos:?}"),
|
||||
Ime::Commit(text) => {
|
||||
self.input_state.append_text(&text);
|
||||
let request_data = self.get_ime_update();
|
||||
window.request_ime_update(ImeRequest::Update(request_data)).unwrap();
|
||||
self.print_input_state();
|
||||
},
|
||||
Ime::DeleteSurrounding { before_bytes, after_bytes } => {
|
||||
let (text, cursor) = &self.input_state.text_and_cursor();
|
||||
|
||||
// To anyone copying this, keep in mind that this doesn't take text
|
||||
// selection into account. The deletion happens
|
||||
// *around* the pre-edit, and may remove the whole
|
||||
// selection or a part of it.
|
||||
let delete_start = cursor.saturating_sub(before_bytes);
|
||||
let delete_end = cmp::min(cursor.saturating_add(after_bytes), text.len());
|
||||
if text.is_char_boundary(delete_start) && text.is_char_boundary(delete_end) {
|
||||
let new_text = {
|
||||
let mut t = String::from(&text[..delete_start]);
|
||||
t.push_str(&text[delete_end..]);
|
||||
t
|
||||
};
|
||||
self.input_state.set_text(new_text);
|
||||
info!("IME deleted bytes: {before_bytes}, {after_bytes}");
|
||||
self.print_input_state();
|
||||
} else {
|
||||
error!("Buggy IME tried to delete with indices not on char boundary.");
|
||||
}
|
||||
},
|
||||
Ime::Disabled => info!("IME disabled for Window={:?}", window.id()),
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_ime(&mut self) {
|
||||
let enable = !self.input_state.ime_enabled;
|
||||
|
||||
if enable {
|
||||
let enable_request = ImeEnableRequest::new(
|
||||
ImeCapabilities::new()
|
||||
.with_hint_and_purpose()
|
||||
.with_cursor_area()
|
||||
.with_surrounding_text(),
|
||||
self.get_ime_update(),
|
||||
)
|
||||
.unwrap();
|
||||
self.window().request_ime_update(ImeRequest::Enable(enable_request)).unwrap();
|
||||
} else {
|
||||
self.window().request_ime_update(ImeRequest::Disable).expect("disable can not fail");
|
||||
};
|
||||
|
||||
self.input_state.ime_enabled = enable;
|
||||
|
||||
info!("IME enabled now {}", self.input_state.ime_enabled);
|
||||
}
|
||||
|
||||
fn get_ime_update(&self) -> ImeRequestData {
|
||||
let text = &self.input_state.contents;
|
||||
let cursor = text.len();
|
||||
// A rudimentary text field emulation: the caret moves right by a constant amount for each
|
||||
// code point.
|
||||
|
||||
let text_before_caret = if text.is_char_boundary(cursor) { &text[..cursor] } else { "" };
|
||||
let chars_before_caret = text_before_caret.chars().count();
|
||||
let cursor_pos = LogicalPosition::<u32> { x: 10 * chars_before_caret as u32, y: 0 };
|
||||
|
||||
// Limit text field size
|
||||
const MAX_BYTES: usize = ImeSurroundingText::MAX_TEXT_BYTES;
|
||||
let minimal_offset = cursor / MAX_BYTES * MAX_BYTES;
|
||||
let first_char_boundary =
|
||||
(minimal_offset..cursor).find(|off| text.is_char_boundary(*off)).unwrap_or(cursor);
|
||||
let last_char_boundary = (cursor..(first_char_boundary + MAX_BYTES))
|
||||
.rev()
|
||||
.find(|off| text.is_char_boundary(*off))
|
||||
.unwrap_or(cursor);
|
||||
let surrounding_text = &text[first_char_boundary..last_char_boundary];
|
||||
let relative_cursor = cursor - first_char_boundary;
|
||||
let surrounding_text =
|
||||
ImeSurroundingText::new(surrounding_text.into(), relative_cursor, relative_cursor)
|
||||
.expect("Bug in example: bad byte calculations");
|
||||
|
||||
ImeRequestData::default()
|
||||
.with_hint_and_purpose(self.input_state.hint, self.input_state.purpose)
|
||||
.with_cursor_area(cursor_pos.into(), IME_CURSOR_SIZE.into())
|
||||
.with_surrounding_text(surrounding_text)
|
||||
}
|
||||
|
||||
fn print_input_state(&self) {
|
||||
let (text, cursor) = &self.input_state.text_and_cursor();
|
||||
// Representing a selection with the cursor and anchor as ends is not
|
||||
// supported yet. Using the same position for anchor to mark no
|
||||
// selection.
|
||||
info!("{}", preedit_with_cursor(text, *cursor, *cursor));
|
||||
}
|
||||
|
||||
fn window(&self) -> &dyn Window {
|
||||
self.window.as_ref().unwrap().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints text of the text field, highlighting cursor position
|
||||
fn preedit_with_cursor(text: &str, cursor: usize, anchor: usize) -> String {
|
||||
preedit_with_cursor_checked(text, cursor, anchor).unwrap_or_else(|e| format!("INVALID: {e}"))
|
||||
}
|
||||
|
||||
fn preedit_with_cursor_checked(text: &str, cursor: usize, anchor: usize) -> Result<String, &str> {
|
||||
let first = cmp::min(cursor, anchor);
|
||||
let before = text.get(0..first).ok_or("first segment ends not on char boundary")?;
|
||||
let second = cmp::max(cursor, anchor);
|
||||
let mid = if second == first {
|
||||
None
|
||||
} else {
|
||||
Some(text.get(first..second).ok_or("second segment ends not on char boundary")?)
|
||||
};
|
||||
let end = text.get(second..).unwrap();
|
||||
Ok(match (first == cursor, before, mid, end) {
|
||||
(_, before, None, end) => format!("{before}|{end}"),
|
||||
(true, before, Some(mid), end) => format!("{before}|{mid}_{end}"),
|
||||
(false, before, Some(mid), end) => format!("{before}_{mid}|{end}"),
|
||||
})
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
#[cfg(web_platform)]
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
tracing_init::init();
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
|
||||
println!(
|
||||
r#"This showcases the use of an input method engine (IME) by emulating a text edit field.
|
||||
Use CTRL+i to toggle IME support.
|
||||
Use CTRL+p to cycle content purpose values.
|
||||
Use CTRL+h to cycle content hint permutations.
|
||||
"#
|
||||
);
|
||||
|
||||
let app = App {
|
||||
window: None,
|
||||
input_state: TextInputState {
|
||||
ime_enabled: true,
|
||||
contents: String::new(),
|
||||
purpose: ImePurpose::Normal,
|
||||
// While we don't show text and thus we use ImeHint::HIDDEN
|
||||
// it may cause the IME to not do layout switch, etc at all.
|
||||
hint: ImeHint::NONE,
|
||||
},
|
||||
modifiers: ModifiersState::default(),
|
||||
};
|
||||
|
||||
// For alternative loop run options see `pump_events` and `run_on_demand` examples.
|
||||
event_loop.run_app(app)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
80
winit/examples/pump_events.rs
Normal file
80
winit/examples/pump_events.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#![allow(clippy::single_match)]
|
||||
|
||||
// Limit this example to only compatible platforms.
|
||||
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, android_platform,))]
|
||||
fn main() -> std::process::ExitCode {
|
||||
use std::process::ExitCode;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::pump_events::{EventLoopExtPumpEvents, PumpStatus};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct PumpDemo {
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for PumpDemo {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default().with_title("A fantastic window!");
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
_window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
println!("{event:?}");
|
||||
|
||||
let window = match self.window.as_ref() {
|
||||
Some(window) => window,
|
||||
None => return,
|
||||
};
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(window.as_ref());
|
||||
window.request_redraw();
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut event_loop = EventLoop::new().unwrap();
|
||||
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let mut app = PumpDemo::default();
|
||||
|
||||
loop {
|
||||
let timeout = Some(Duration::ZERO);
|
||||
let status = event_loop.pump_app_events(timeout, &mut app);
|
||||
|
||||
if let PumpStatus::Exit(exit_code) = status {
|
||||
break ExitCode::from(exit_code as u8);
|
||||
}
|
||||
|
||||
// Sleep for 1/60 second to simulate application work
|
||||
//
|
||||
// Since `pump_events` doesn't block it will be important to
|
||||
// throttle the loop in the app somehow.
|
||||
println!("Update()");
|
||||
sleep(Duration::from_millis(16));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(ios_platform, web_platform, orbital_platform))]
|
||||
fn main() {
|
||||
println!("This platform doesn't support pump_events.");
|
||||
}
|
||||
99
winit/examples/run_on_demand.rs
Normal file
99
winit/examples/run_on_demand.rs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#![allow(clippy::single_match)]
|
||||
|
||||
// Limit this example to only compatible platforms.
|
||||
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform,))]
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
use std::time::Duration;
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::run_on_demand::EventLoopExtRunOnDemand;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct App {
|
||||
idx: usize,
|
||||
window_id: Option<WindowId>,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
||||
if let Some(window) = self.window.as_ref() {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let window_attributes = WindowAttributes::default()
|
||||
.with_title("Fantastic window number one!")
|
||||
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
|
||||
let window = event_loop.create_window(window_attributes).unwrap();
|
||||
self.window_id = Some(window.id());
|
||||
self.window = Some(window);
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
if event == WindowEvent::Destroyed && self.window_id == Some(window_id) {
|
||||
println!(
|
||||
"--------------------------------------------------------- Window {} Destroyed",
|
||||
self.idx
|
||||
);
|
||||
self.window_id = None;
|
||||
event_loop.exit();
|
||||
return;
|
||||
}
|
||||
|
||||
let window = match self.window.as_mut() {
|
||||
Some(window) => window,
|
||||
None => return,
|
||||
};
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
println!(
|
||||
"--------------------------------------------------------- Window {} \
|
||||
CloseRequested",
|
||||
self.idx
|
||||
);
|
||||
fill::cleanup_window(window.as_ref());
|
||||
self.window = None;
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let mut event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let mut app = App { idx: 1, ..Default::default() };
|
||||
event_loop.run_app_on_demand(&mut app)?;
|
||||
|
||||
println!("--------------------------------------------------------- Finished first loop");
|
||||
println!("--------------------------------------------------------- Waiting 5 seconds");
|
||||
std::thread::sleep(Duration::from_secs(5));
|
||||
|
||||
app.idx += 1;
|
||||
event_loop.run_app_on_demand(&mut app)?;
|
||||
println!("--------------------------------------------------------- Finished second loop");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(windows_platform, macos_platform, x11_platform, wayland_platform,)))]
|
||||
fn main() {
|
||||
println!("This example is not supported on this platform");
|
||||
}
|
||||
156
winit/examples/util/fill.rs
Normal file
156
winit/examples/util/fill.rs
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
//! Fill the window buffer with a solid color.
|
||||
//!
|
||||
//! Launching a window without drawing to it has unpredictable results varying from platform to
|
||||
//! platform. In order to have well-defined examples, this module provides an easy way to
|
||||
//! fill the window buffer with a solid color.
|
||||
//!
|
||||
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
|
||||
//! also be used to fill the window buffer, but they are more complicated to use.
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use platform::cleanup_window;
|
||||
#[allow(unused_imports)]
|
||||
pub use platform::fill_window;
|
||||
#[allow(unused_imports)]
|
||||
pub use platform::fill_window_with_animated_color;
|
||||
#[allow(unused_imports)]
|
||||
pub use platform::fill_window_with_color;
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
mod platform {
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::num::NonZeroU32;
|
||||
#[cfg(all(not(android_platform), not(web_platform)))]
|
||||
use std::time::Instant;
|
||||
|
||||
use softbuffer::{Context, Surface};
|
||||
#[cfg(all(web_platform, not(android_platform)))]
|
||||
use web_time::Instant;
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
thread_local! {
|
||||
// NOTE: You should never do things like that, create context and drop it before
|
||||
// you drop the event loop. We do this for brevity to not blow up examples. We use
|
||||
// ManuallyDrop to prevent destructors from running.
|
||||
//
|
||||
// A static, thread-local map of graphics contexts to open windows.
|
||||
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = const { ManuallyDrop::new(RefCell::new(None)) };
|
||||
}
|
||||
|
||||
/// The graphics context used to draw to a window.
|
||||
struct GraphicsContext {
|
||||
/// The global softbuffer context.
|
||||
context: RefCell<Context<&'static dyn Window>>,
|
||||
|
||||
/// The hash map of window IDs to surfaces.
|
||||
surfaces: HashMap<WindowId, Surface<&'static dyn Window, &'static dyn Window>>,
|
||||
}
|
||||
|
||||
impl GraphicsContext {
|
||||
fn new(w: &dyn Window) -> Self {
|
||||
Self {
|
||||
context: RefCell::new(
|
||||
Context::new(unsafe {
|
||||
mem::transmute::<&'_ dyn Window, &'static dyn Window>(w)
|
||||
})
|
||||
.expect("Failed to create a softbuffer context"),
|
||||
),
|
||||
surfaces: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_surface(
|
||||
&mut self,
|
||||
window: &dyn Window,
|
||||
) -> &mut Surface<&'static dyn Window, &'static dyn Window> {
|
||||
self.surfaces.entry(window.id()).or_insert_with(|| {
|
||||
Surface::new(&self.context.borrow(), unsafe {
|
||||
mem::transmute::<&'_ dyn Window, &'static dyn Window>(window)
|
||||
})
|
||||
.expect("Failed to create a softbuffer surface")
|
||||
})
|
||||
}
|
||||
|
||||
fn destroy_surface(&mut self, window: &dyn Window) {
|
||||
self.surfaces.remove(&window.id());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill_window_with_color(window: &dyn Window, color: u32) {
|
||||
GC.with(|gc| {
|
||||
let size = window.surface_size();
|
||||
let (Some(width), Some(height)) =
|
||||
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Either get the last context used or create a new one.
|
||||
let mut gc = gc.borrow_mut();
|
||||
let surface =
|
||||
gc.get_or_insert_with(|| GraphicsContext::new(window)).create_surface(window);
|
||||
|
||||
// Fill a buffer with a solid color
|
||||
|
||||
surface.resize(width, height).expect("Failed to resize the softbuffer surface");
|
||||
|
||||
let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer");
|
||||
buffer.fill(color);
|
||||
buffer.present().expect("Failed to present the softbuffer buffer");
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn fill_window(window: &dyn Window) {
|
||||
fill_window_with_color(window, 0xff181818);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn fill_window_with_animated_color(window: &dyn Window, start: Instant) {
|
||||
let time = start.elapsed().as_secs_f32() * 1.5;
|
||||
let blue = (time.sin() * 255.0) as u32;
|
||||
let green = ((time.cos() * 255.0) as u32) << 8;
|
||||
let red = ((1.0 - time.sin() * 255.0) as u32) << 16;
|
||||
let color = red | green | blue;
|
||||
fill_window_with_color(window, color);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup_window(window: &dyn Window) {
|
||||
GC.with(|gc| {
|
||||
let mut gc = gc.borrow_mut();
|
||||
if let Some(context) = gc.as_mut() {
|
||||
context.destroy_surface(window);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
mod platform {
|
||||
#[allow(dead_code)]
|
||||
pub fn fill_window(_window: &dyn winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn fill_window_with_color(_window: &dyn winit::window::Window, _color: u32) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn fill_window_with_animated_color(
|
||||
_window: &dyn winit::window::Window,
|
||||
_start: std::time::Instant,
|
||||
) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup_window(_window: &dyn winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
}
|
||||
25
winit/examples/util/tracing.rs
Normal file
25
winit/examples/util/tracing.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#[cfg(not(web_platform))]
|
||||
pub fn init() {
|
||||
use tracing_subscriber::filter::{EnvFilter, LevelFilter};
|
||||
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
EnvFilter::builder().with_default_directive(LevelFilter::INFO.into()).from_env_lossy(),
|
||||
)
|
||||
.init();
|
||||
}
|
||||
|
||||
#[cfg(web_platform)]
|
||||
pub fn init() {
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::fmt::layer()
|
||||
.with_ansi(false)
|
||||
.without_time()
|
||||
.with_writer(tracing_web::MakeWebConsoleWriter::new()),
|
||||
)
|
||||
.init();
|
||||
}
|
||||
84
winit/examples/window.rs
Normal file
84
winit/examples/window.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
//! Simple winit window example.
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
#[cfg(web_platform)]
|
||||
use winit::platform::web::WindowAttributesWeb;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
#[path = "util/tracing.rs"]
|
||||
mod tracing;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct App {
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
#[cfg(not(web_platform))]
|
||||
let window_attributes = WindowAttributes::default();
|
||||
#[cfg(web_platform)]
|
||||
let window_attributes = WindowAttributes::default()
|
||||
.with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true)));
|
||||
self.window = match event_loop.create_window(window_attributes) {
|
||||
Ok(window) => Some(window),
|
||||
Err(err) => {
|
||||
eprintln!("error creating window: {err}");
|
||||
event_loop.exit();
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) {
|
||||
println!("{event:?}");
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
println!("Close was requested; stopping");
|
||||
event_loop.exit();
|
||||
},
|
||||
WindowEvent::SurfaceResized(_) => {
|
||||
self.window.as_ref().expect("resize event without a window").request_redraw();
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
// Redraw the application.
|
||||
//
|
||||
// It's preferable for applications that do not render continuously to render in
|
||||
// this event rather than in AboutToWait, since rendering in here allows
|
||||
// the program to gracefully handle redraws requested by the OS.
|
||||
|
||||
let window = self.window.as_ref().expect("redraw request without a window");
|
||||
|
||||
// Notify that you're about to draw.
|
||||
window.pre_present_notify();
|
||||
|
||||
// Draw.
|
||||
fill::fill_window(window.as_ref());
|
||||
|
||||
// For contiguous redraw loop you can request a redraw from here.
|
||||
// window.request_redraw();
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
#[cfg(web_platform)]
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
tracing::init();
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
|
||||
// For alternative loop run options see `pump_events` and `run_on_demand` examples.
|
||||
event_loop.run_app(App::default())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
71
winit/examples/x11_embed.rs
Normal file
71
winit/examples/x11_embed.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
//! A demonstration of embedding a winit window in an existing X11 application.
|
||||
use std::error::Error;
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::platform::x11::WindowAttributesX11;
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XEmbedDemo {
|
||||
parent_window_id: u32,
|
||||
window: Option<Box<dyn Window>>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for XEmbedDemo {
|
||||
fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||
let mut window_attributes = WindowAttributes::default()
|
||||
.with_title("An embedded window!")
|
||||
.with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0));
|
||||
let x11_attrs =
|
||||
WindowAttributesX11::default().with_embed_parent_window(self.parent_window_id);
|
||||
window_attributes = window_attributes.with_platform_attributes(Box::new(x11_attrs));
|
||||
|
||||
self.window = Some(event_loop.create_window(window_attributes).unwrap());
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
_window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
let window = self.window.as_ref().unwrap();
|
||||
match event {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(window.as_ref());
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
|
||||
self.window.as_ref().unwrap().request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
// First argument should be a 32-bit X11 window ID.
|
||||
let parent_window_id = std::env::args()
|
||||
.nth(1)
|
||||
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
|
||||
.parse::<u32>()?;
|
||||
|
||||
tracing_subscriber::fmt::init();
|
||||
let event_loop = EventLoop::new()?;
|
||||
|
||||
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?)
|
||||
}
|
||||
|
||||
#[cfg(not(x11_platform))]
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
println!("This example is only supported on X11 platforms.");
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -84,6 +84,8 @@ changelog entry.
|
|||
- On Wayland, added implementation for `Window::set_window_icon`
|
||||
- Add `Window::request_ime_update` to atomically apply set of IME changes.
|
||||
- Add `Ime::DeleteSurrounding` to let the input method delete text.
|
||||
- Add more `ImePurpose` values.
|
||||
- Add `ImeHints` to request particular IME behaviour.
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue