mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Remove a level of indirection in main window example
We already have the `Action` enum that abstracts key and mouse event bindings; on top of that, putting each action handler in its own functions seems excessive.
This commit is contained in:
@@ -15,7 +15,7 @@ use rwh_05::HasRawDisplayHandle;
|
||||
use softbuffer::{Context, Surface};
|
||||
|
||||
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
|
||||
use winit::event::{DeviceEvent, DeviceId, Event, Ime, WindowEvent};
|
||||
use winit::event::{DeviceEvent, DeviceId, ElementState, Event, Ime, KeyEvent, WindowEvent};
|
||||
use winit::event::{MouseButton, MouseScrollDelta};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::keyboard::{Key, ModifiersState};
|
||||
@@ -51,7 +51,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
});
|
||||
|
||||
let mut state = Application::new(&event_loop);
|
||||
let mut app = Application::new(&event_loop);
|
||||
|
||||
event_loop.run(move |event, event_loop| match event {
|
||||
Event::NewEvents(_) => (),
|
||||
@@ -59,23 +59,22 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
println!("Resumed the event loop");
|
||||
|
||||
// Create initial window.
|
||||
state
|
||||
.create_window(event_loop, None)
|
||||
app.create_window(event_loop, None)
|
||||
.expect("failed to create initial window");
|
||||
|
||||
state.print_help();
|
||||
app.print_help();
|
||||
}
|
||||
Event::AboutToWait => {
|
||||
if state.windows.is_empty() {
|
||||
if app.windows.is_empty() {
|
||||
println!("No windows left, exiting...");
|
||||
event_loop.exit();
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
state.handle_window_event(event_loop, window_id, event)
|
||||
app.handle_window_event(event_loop, window_id, event)
|
||||
}
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
state.handle_device_event(event_loop, device_id, event)
|
||||
app.handle_device_event(event_loop, device_id, event)
|
||||
}
|
||||
Event::UserEvent(event) => {
|
||||
println!("User event: {event:?}");
|
||||
@@ -172,16 +171,62 @@ impl Application {
|
||||
window.recognize_rotation_gesture(true);
|
||||
}
|
||||
|
||||
let window_state = WindowState::new(self, window)?;
|
||||
let window_id = window_state.window.id();
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
let surface = {
|
||||
// SAFETY: the surface is dropped before the `window` which
|
||||
// provided it with handle, thus it doesn't outlive it.
|
||||
let mut surface = unsafe { Surface::new(&self.context, &window)? };
|
||||
|
||||
let size = window.inner_size();
|
||||
if let (Some(width), Some(height)) =
|
||||
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
{
|
||||
surface
|
||||
.resize(width, height)
|
||||
.expect("failed to resize inner buffer");
|
||||
};
|
||||
|
||||
surface
|
||||
};
|
||||
|
||||
let theme = window.theme().unwrap_or(Theme::Dark);
|
||||
println!("Theme: {theme:?}");
|
||||
let named_idx = 0;
|
||||
window.set_cursor(CURSORS[named_idx]);
|
||||
|
||||
// Allow IME out of the box.
|
||||
let ime = true;
|
||||
window.set_ime_allowed(ime);
|
||||
|
||||
let state = WindowState {
|
||||
#[cfg(macos_platform)]
|
||||
option_as_alt: window.option_as_alt(),
|
||||
window,
|
||||
custom_idx: self.custom_cursors.len() - 1,
|
||||
cursor_grab: CursorGrabMode::None,
|
||||
named_idx,
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
surface,
|
||||
theme,
|
||||
ime,
|
||||
cursor_position: Default::default(),
|
||||
cursor_hidden: Default::default(),
|
||||
modifiers: Default::default(),
|
||||
occluded: Default::default(),
|
||||
rotated: Default::default(),
|
||||
zoom: Default::default(),
|
||||
};
|
||||
|
||||
let window_id = state.window.id();
|
||||
println!("Created new window with id={window_id:?}");
|
||||
self.windows.insert(window_id, window_state);
|
||||
self.windows.insert(window_id, state);
|
||||
Ok(window_id)
|
||||
}
|
||||
|
||||
fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) {
|
||||
// let cursor_position = self.cursor_position;
|
||||
let window = self.windows.get_mut(&window_id).unwrap();
|
||||
let state = self.windows.get_mut(&window_id).unwrap();
|
||||
let window = &state.window;
|
||||
|
||||
println!("Executing action: {action:?}");
|
||||
match action {
|
||||
Action::CloseWindow => {
|
||||
@@ -189,7 +234,7 @@ impl Application {
|
||||
}
|
||||
Action::CreateNewWindow => {
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
if let Err(err) = window.window.request_activation_token() {
|
||||
if let Err(err) = window.request_activation_token() {
|
||||
println!("Failed to get activation token: {err}");
|
||||
} else {
|
||||
return;
|
||||
@@ -199,26 +244,146 @@ impl Application {
|
||||
eprintln!("Error creating new window: {err}");
|
||||
}
|
||||
}
|
||||
Action::ToggleResizeIncrements => window.toggle_resize_increments(),
|
||||
Action::ToggleCursorVisibility => window.toggle_cursor_visibility(),
|
||||
Action::ToggleResizable => window.toggle_resizable(),
|
||||
Action::ToggleDecorations => window.toggle_decorations(),
|
||||
Action::ToggleFullscreen => window.toggle_fullscreen(),
|
||||
Action::ToggleMaximize => window.toggle_maximize(),
|
||||
Action::ToggleImeInput => window.toggle_ime(),
|
||||
Action::Minimize => window.minimize(),
|
||||
Action::NextCursor => window.next_cursor(),
|
||||
Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors),
|
||||
Action::CycleCursorGrab => window.cycle_cursor_grab(),
|
||||
Action::DragWindow => window.drag_window(),
|
||||
Action::DragResizeWindow => window.drag_resize_window(),
|
||||
Action::ShowWindowMenu => window.show_menu(),
|
||||
Action::ToggleResizeIncrements => {
|
||||
let new_increments = match window.resize_increments() {
|
||||
Some(_) => None,
|
||||
None => Some(LogicalSize::new(25.0, 25.0)),
|
||||
};
|
||||
println!("Had increments: {}", new_increments.is_none());
|
||||
window.set_resize_increments(new_increments);
|
||||
}
|
||||
Action::ToggleCursorVisibility => {
|
||||
state.cursor_hidden = !state.cursor_hidden;
|
||||
window.set_cursor_visible(!state.cursor_hidden);
|
||||
}
|
||||
Action::ToggleResizable => {
|
||||
let resizable = window.is_resizable();
|
||||
window.set_resizable(!resizable);
|
||||
}
|
||||
Action::ToggleDecorations => {
|
||||
let decorated = window.is_decorated();
|
||||
window.set_decorations(!decorated);
|
||||
}
|
||||
Action::ToggleFullscreen => {
|
||||
let fullscreen = if window.fullscreen().is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(Fullscreen::Borderless(None))
|
||||
};
|
||||
|
||||
window.set_fullscreen(fullscreen);
|
||||
}
|
||||
Action::ToggleMaximize => {
|
||||
let maximized = window.is_maximized();
|
||||
window.set_maximized(!maximized);
|
||||
}
|
||||
Action::ToggleImeInput => {
|
||||
state.ime = !state.ime;
|
||||
window.set_ime_allowed(state.ime);
|
||||
if let Some(position) = state.ime.then_some(state.cursor_position).flatten() {
|
||||
window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
|
||||
}
|
||||
}
|
||||
Action::Minimize => {
|
||||
window.set_minimized(true);
|
||||
}
|
||||
Action::NextCursor => {
|
||||
// Pick the next cursor
|
||||
state.named_idx = (state.named_idx + 1) % CURSORS.len();
|
||||
println!("Setting cursor to \"{:?}\"", CURSORS[state.named_idx]);
|
||||
window.set_cursor(Cursor::Icon(CURSORS[state.named_idx]));
|
||||
}
|
||||
Action::NextCustomCursor => {
|
||||
state.custom_idx = (state.custom_idx + 1) % self.custom_cursors.len();
|
||||
let cursor = Cursor::Custom(self.custom_cursors[state.custom_idx].clone());
|
||||
window.set_cursor(cursor);
|
||||
}
|
||||
Action::CycleCursorGrab => {
|
||||
state.cursor_grab = match state.cursor_grab {
|
||||
CursorGrabMode::None => CursorGrabMode::Confined,
|
||||
CursorGrabMode::Confined => CursorGrabMode::Locked,
|
||||
CursorGrabMode::Locked => CursorGrabMode::None,
|
||||
};
|
||||
println!("Changing cursor grab mode to {:?}", state.cursor_grab);
|
||||
if let Err(err) = window.set_cursor_grab(state.cursor_grab) {
|
||||
eprintln!("Error setting cursor grab: {err}");
|
||||
}
|
||||
}
|
||||
Action::DragWindow => {
|
||||
if let Err(err) = window.drag_window() {
|
||||
println!("Error starting window drag: {err}");
|
||||
} else {
|
||||
println!("Dragging window Window={:?}", window.id());
|
||||
}
|
||||
}
|
||||
Action::DragResizeWindow => {
|
||||
let position = match state.cursor_position {
|
||||
Some(position) => position,
|
||||
None => {
|
||||
println!("Drag-resize requires cursor to be inside the window");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let win_size = window.inner_size();
|
||||
let border_size = BORDER_SIZE * window.scale_factor();
|
||||
|
||||
let x_direction = if position.x < border_size {
|
||||
ResizeDirection::West
|
||||
} else if position.x > (win_size.width as f64 - border_size) {
|
||||
ResizeDirection::East
|
||||
} else {
|
||||
// Use arbitrary direction instead of None for simplicity.
|
||||
ResizeDirection::SouthEast
|
||||
};
|
||||
|
||||
let y_direction = if position.y < border_size {
|
||||
ResizeDirection::North
|
||||
} else if position.y > (win_size.height as f64 - border_size) {
|
||||
ResizeDirection::South
|
||||
} else {
|
||||
// Use arbitrary direction instead of None for simplicity.
|
||||
ResizeDirection::SouthEast
|
||||
};
|
||||
|
||||
let direction = match (x_direction, y_direction) {
|
||||
(ResizeDirection::West, ResizeDirection::North) => ResizeDirection::NorthWest,
|
||||
(ResizeDirection::West, ResizeDirection::South) => ResizeDirection::SouthWest,
|
||||
(ResizeDirection::West, _) => ResizeDirection::West,
|
||||
(ResizeDirection::East, ResizeDirection::North) => ResizeDirection::NorthEast,
|
||||
(ResizeDirection::East, ResizeDirection::South) => ResizeDirection::SouthEast,
|
||||
(ResizeDirection::East, _) => ResizeDirection::East,
|
||||
(_, ResizeDirection::South) => ResizeDirection::South,
|
||||
(_, ResizeDirection::North) => ResizeDirection::North,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if let Err(err) = window.drag_resize_window(direction) {
|
||||
println!("Error starting window drag-resize: {err}");
|
||||
} else {
|
||||
println!("Drag-resizing window Window={:?}", window.id());
|
||||
}
|
||||
}
|
||||
Action::ShowWindowMenu => {
|
||||
if let Some(position) = state.cursor_position {
|
||||
window.show_window_menu(position);
|
||||
}
|
||||
}
|
||||
Action::PrintHelp => self.print_help(),
|
||||
#[cfg(macos_platform)]
|
||||
Action::CycleOptionAsAlt => window.cycle_option_as_alt(),
|
||||
Action::CycleOptionAsAlt => {
|
||||
state.option_as_alt = match state.option_as_alt {
|
||||
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
|
||||
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
|
||||
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
|
||||
OptionAsAlt::Both => OptionAsAlt::None,
|
||||
};
|
||||
println!("Setting option as alt {:?}", state.option_as_alt);
|
||||
window.set_option_as_alt(state.option_as_alt);
|
||||
}
|
||||
#[cfg(macos_platform)]
|
||||
Action::CreateNewTab => {
|
||||
let tab_id = window.window.tabbing_identifier();
|
||||
let tab_id = window.tabbing_identifier();
|
||||
if let Err(err) = self.create_window(event_loop, Some(tab_id)) {
|
||||
eprintln!("Error creating new window: {err}");
|
||||
}
|
||||
@@ -232,14 +397,28 @@ impl Application {
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
let window = match self.windows.get_mut(&window_id) {
|
||||
Some(window) => window,
|
||||
let state = match self.windows.get_mut(&window_id) {
|
||||
Some(state) => state,
|
||||
None => return,
|
||||
};
|
||||
let window = &state.window;
|
||||
|
||||
match event {
|
||||
WindowEvent::Resized(size) => {
|
||||
window.resize(size);
|
||||
// Resize the surface to the new size
|
||||
WindowEvent::Resized(_size) => {
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
{
|
||||
let (width, height) =
|
||||
match (NonZeroU32::new(_size.width), NonZeroU32::new(_size.height)) {
|
||||
(Some(width), Some(height)) => (width, height),
|
||||
_ => return,
|
||||
};
|
||||
state
|
||||
.surface
|
||||
.resize(width, height)
|
||||
.expect("failed to resize inner buffer");
|
||||
}
|
||||
window.request_redraw();
|
||||
}
|
||||
WindowEvent::Focused(focused) => {
|
||||
if focused {
|
||||
@@ -253,23 +432,51 @@ impl Application {
|
||||
}
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
println!("Theme changed to {theme:?}");
|
||||
window.set_theme(theme);
|
||||
state.theme = theme;
|
||||
window.request_redraw();
|
||||
}
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Err(err) = window.draw() {
|
||||
eprintln!("Error drawing window: {err}");
|
||||
// Draw the window contents.
|
||||
|
||||
if state.occluded {
|
||||
println!("Skipping drawing occluded window={:?}", window_id);
|
||||
}
|
||||
|
||||
const WHITE: u32 = 0xFFFFFFFF;
|
||||
const DARK_GRAY: u32 = 0xFF181818;
|
||||
|
||||
let color = match state.theme {
|
||||
Theme::Light => WHITE,
|
||||
Theme::Dark => DARK_GRAY,
|
||||
};
|
||||
|
||||
let mut buffer = state
|
||||
.surface
|
||||
.buffer_mut()
|
||||
.expect("could not retrieve buffer");
|
||||
buffer.fill(color);
|
||||
window.pre_present_notify();
|
||||
buffer.present().expect("failed presenting to window");
|
||||
}
|
||||
#[cfg(any(android_platform, ios_platform))]
|
||||
WindowEvent::RedrawRequested => {
|
||||
println!("Drawing but without rendering...");
|
||||
}
|
||||
// Change window occlusion state.
|
||||
WindowEvent::Occluded(occluded) => {
|
||||
window.set_occluded(occluded);
|
||||
state.occluded = occluded;
|
||||
if !occluded {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
println!("Closing Window={window_id:?}");
|
||||
self.windows.remove(&window_id);
|
||||
}
|
||||
WindowEvent::ModifiersChanged(modifiers) => {
|
||||
window.modifiers = modifiers.state();
|
||||
println!("Modifiers changed to {:?}", window.modifiers);
|
||||
state.modifiers = modifiers.state();
|
||||
println!("Modifiers changed to {:?}", state.modifiers);
|
||||
}
|
||||
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||
MouseScrollDelta::LineDelta(x, y) => {
|
||||
@@ -280,42 +487,44 @@ impl Application {
|
||||
}
|
||||
},
|
||||
WindowEvent::KeyboardInput {
|
||||
event,
|
||||
event:
|
||||
KeyEvent {
|
||||
// Dispatch actions only on press.
|
||||
state: ElementState::Pressed,
|
||||
logical_key,
|
||||
..
|
||||
},
|
||||
is_synthetic: false,
|
||||
..
|
||||
} => {
|
||||
let mods = window.modifiers;
|
||||
|
||||
// Dispatch actions only on press.
|
||||
if event.state.is_pressed() {
|
||||
let action = if let Key::Character(ch) = event.logical_key.as_ref() {
|
||||
Self::process_key_binding(&ch.to_uppercase(), &mods)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(action) = action {
|
||||
if let Key::Character(ch) = logical_key.as_ref() {
|
||||
let mods = state.modifiers;
|
||||
if let Some(action) = Self::process_key_binding(&ch.to_uppercase(), &mods) {
|
||||
self.handle_action(event_loop, window_id, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseInput { button, state, .. } => {
|
||||
let mods = window.modifiers;
|
||||
if let Some(action) = state
|
||||
.is_pressed()
|
||||
.then(|| Self::process_mouse_binding(button, &mods))
|
||||
.flatten()
|
||||
{
|
||||
WindowEvent::KeyboardInput { .. } => {}
|
||||
WindowEvent::MouseInput {
|
||||
button,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => {
|
||||
if let Some(action) = Self::process_mouse_binding(button, &state.modifiers) {
|
||||
self.handle_action(event_loop, window_id, action);
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseInput { .. } => {}
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
println!("Cursor left Window={window_id:?}");
|
||||
window.cursor_left();
|
||||
state.cursor_position = None;
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
println!("Moved cursor to {position:?}");
|
||||
window.cursor_moved(position);
|
||||
state.cursor_position = Some(position);
|
||||
if state.ime {
|
||||
window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
|
||||
}
|
||||
}
|
||||
WindowEvent::ActivationTokenDone { token: _token, .. } => {
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
@@ -337,8 +546,8 @@ impl Application {
|
||||
Ime::Disabled => println!("IME disabled for Window={window_id:?}"),
|
||||
},
|
||||
WindowEvent::PinchGesture { delta, .. } => {
|
||||
window.zoom += delta;
|
||||
let zoom = window.zoom;
|
||||
state.zoom += delta;
|
||||
let zoom = state.zoom;
|
||||
if delta > 0.0 {
|
||||
println!("Zoomed in {delta:.5} (now: {zoom:.5})");
|
||||
} else {
|
||||
@@ -346,8 +555,8 @@ impl Application {
|
||||
}
|
||||
}
|
||||
WindowEvent::RotationGesture { delta, .. } => {
|
||||
window.rotated += delta;
|
||||
let rotated = window.rotated;
|
||||
state.rotated += delta;
|
||||
let rotated = state.rotated;
|
||||
if delta > 0.0 {
|
||||
println!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})");
|
||||
} else {
|
||||
@@ -359,7 +568,6 @@ impl Application {
|
||||
}
|
||||
WindowEvent::TouchpadPressure { .. }
|
||||
| WindowEvent::HoveredFileCancelled
|
||||
| WindowEvent::KeyboardInput { .. }
|
||||
| WindowEvent::CursorEntered { .. }
|
||||
| WindowEvent::AxisMotion { .. }
|
||||
| WindowEvent::DroppedFile(_)
|
||||
@@ -416,8 +624,10 @@ impl Application {
|
||||
}
|
||||
}
|
||||
|
||||
/// State of the window.
|
||||
/// Extra state on a window used in this example.
|
||||
struct WindowState {
|
||||
/// The actual Winit window.
|
||||
window: Window,
|
||||
/// IME input.
|
||||
ime: bool,
|
||||
/// Render surface.
|
||||
@@ -425,8 +635,6 @@ struct WindowState {
|
||||
/// NOTE: This surface must be dropped before the `Window`.
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
surface: Surface,
|
||||
/// The actual winit Window.
|
||||
window: Window,
|
||||
/// The window theme we're drawing with.
|
||||
theme: Theme,
|
||||
/// Cursor position over the window.
|
||||
@@ -451,282 +659,6 @@ struct WindowState {
|
||||
cursor_hidden: bool,
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
fn new(application: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
|
||||
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus
|
||||
// it doesn't outlive it.
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
let surface = unsafe { Surface::new(&application.context, &window)? };
|
||||
|
||||
let theme = window.theme().unwrap_or(Theme::Dark);
|
||||
println!("Theme: {theme:?}");
|
||||
let named_idx = 0;
|
||||
window.set_cursor(CURSORS[named_idx]);
|
||||
|
||||
// Allow IME out of the box.
|
||||
let ime = true;
|
||||
window.set_ime_allowed(ime);
|
||||
|
||||
let size = window.inner_size();
|
||||
let mut state = Self {
|
||||
#[cfg(macos_platform)]
|
||||
option_as_alt: window.option_as_alt(),
|
||||
custom_idx: application.custom_cursors.len() - 1,
|
||||
cursor_grab: CursorGrabMode::None,
|
||||
named_idx,
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
surface,
|
||||
window,
|
||||
theme,
|
||||
ime,
|
||||
cursor_position: Default::default(),
|
||||
cursor_hidden: Default::default(),
|
||||
modifiers: Default::default(),
|
||||
occluded: Default::default(),
|
||||
rotated: Default::default(),
|
||||
zoom: Default::default(),
|
||||
};
|
||||
|
||||
state.resize(size);
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub fn toggle_ime(&mut self) {
|
||||
self.ime = !self.ime;
|
||||
self.window.set_ime_allowed(self.ime);
|
||||
if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
|
||||
self.window
|
||||
.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn minimize(&mut self) {
|
||||
self.window.set_minimized(true);
|
||||
}
|
||||
|
||||
pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
|
||||
self.cursor_position = Some(position);
|
||||
if self.ime {
|
||||
self.window
|
||||
.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cursor_left(&mut self) {
|
||||
self.cursor_position = None;
|
||||
}
|
||||
|
||||
/// Toggle maximized.
|
||||
fn toggle_maximize(&self) {
|
||||
let maximized = self.window.is_maximized();
|
||||
self.window.set_maximized(!maximized);
|
||||
}
|
||||
|
||||
/// Toggle window decorations.
|
||||
fn toggle_decorations(&self) {
|
||||
let decorated = self.window.is_decorated();
|
||||
self.window.set_decorations(!decorated);
|
||||
}
|
||||
|
||||
/// Toggle window resizable state.
|
||||
fn toggle_resizable(&self) {
|
||||
let resizable = self.window.is_resizable();
|
||||
self.window.set_resizable(!resizable);
|
||||
}
|
||||
|
||||
/// Toggle cursor visibility
|
||||
fn toggle_cursor_visibility(&mut self) {
|
||||
self.cursor_hidden = !self.cursor_hidden;
|
||||
self.window.set_cursor_visible(!self.cursor_hidden);
|
||||
}
|
||||
|
||||
/// Toggle resize increments on a window.
|
||||
fn toggle_resize_increments(&mut self) {
|
||||
let new_increments = match self.window.resize_increments() {
|
||||
Some(_) => None,
|
||||
None => Some(LogicalSize::new(25.0, 25.0)),
|
||||
};
|
||||
println!("Had increments: {}", new_increments.is_none());
|
||||
self.window.set_resize_increments(new_increments);
|
||||
}
|
||||
|
||||
/// Toggle fullscreen.
|
||||
fn toggle_fullscreen(&self) {
|
||||
let fullscreen = if self.window.fullscreen().is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(Fullscreen::Borderless(None))
|
||||
};
|
||||
|
||||
self.window.set_fullscreen(fullscreen);
|
||||
}
|
||||
|
||||
/// Cycle through the grab modes ignoring errors.
|
||||
fn cycle_cursor_grab(&mut self) {
|
||||
self.cursor_grab = match self.cursor_grab {
|
||||
CursorGrabMode::None => CursorGrabMode::Confined,
|
||||
CursorGrabMode::Confined => CursorGrabMode::Locked,
|
||||
CursorGrabMode::Locked => CursorGrabMode::None,
|
||||
};
|
||||
println!("Changing cursor grab mode to {:?}", self.cursor_grab);
|
||||
if let Err(err) = self.window.set_cursor_grab(self.cursor_grab) {
|
||||
eprintln!("Error setting cursor grab: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(macos_platform)]
|
||||
fn cycle_option_as_alt(&mut self) {
|
||||
self.option_as_alt = match self.option_as_alt {
|
||||
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
|
||||
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
|
||||
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
|
||||
OptionAsAlt::Both => OptionAsAlt::None,
|
||||
};
|
||||
println!("Setting option as alt {:?}", self.option_as_alt);
|
||||
self.window.set_option_as_alt(self.option_as_alt);
|
||||
}
|
||||
|
||||
/// Pick the next cursor.
|
||||
fn next_cursor(&mut self) {
|
||||
self.named_idx = (self.named_idx + 1) % CURSORS.len();
|
||||
println!("Setting cursor to \"{:?}\"", CURSORS[self.named_idx]);
|
||||
self.window
|
||||
.set_cursor(Cursor::Icon(CURSORS[self.named_idx]));
|
||||
}
|
||||
|
||||
/// Pick the next custom cursor.
|
||||
fn next_custom_cursor(&mut self, custom_cursors: &[CustomCursor]) {
|
||||
self.custom_idx = (self.custom_idx + 1) % custom_cursors.len();
|
||||
let cursor = Cursor::Custom(custom_cursors[self.custom_idx].clone());
|
||||
self.window.set_cursor(cursor);
|
||||
}
|
||||
|
||||
/// Resize the window to the new size.
|
||||
fn resize(&mut self, _size: PhysicalSize<u32>) {
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
{
|
||||
let (width, height) =
|
||||
match (NonZeroU32::new(_size.width), NonZeroU32::new(_size.height)) {
|
||||
(Some(width), Some(height)) => (width, height),
|
||||
_ => return,
|
||||
};
|
||||
self.surface
|
||||
.resize(width, height)
|
||||
.expect("failed to resize inner buffer");
|
||||
}
|
||||
self.window.request_redraw();
|
||||
}
|
||||
|
||||
/// Change the theme.
|
||||
fn set_theme(&mut self, theme: Theme) {
|
||||
self.theme = theme;
|
||||
self.window.request_redraw();
|
||||
}
|
||||
|
||||
/// Show window menu.
|
||||
fn show_menu(&self) {
|
||||
if let Some(position) = self.cursor_position {
|
||||
self.window.show_window_menu(position);
|
||||
}
|
||||
}
|
||||
|
||||
/// Drag the window.
|
||||
fn drag_window(&self) {
|
||||
if let Err(err) = self.window.drag_window() {
|
||||
println!("Error starting window drag: {err}");
|
||||
} else {
|
||||
println!("Dragging window Window={:?}", self.window.id());
|
||||
}
|
||||
}
|
||||
|
||||
/// Drag-resize the window.
|
||||
fn drag_resize_window(&self) {
|
||||
let position = match self.cursor_position {
|
||||
Some(position) => position,
|
||||
None => {
|
||||
println!("Drag-resize requires cursor to be inside the window");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let win_size = self.window.inner_size();
|
||||
let border_size = BORDER_SIZE * self.window.scale_factor();
|
||||
|
||||
let x_direction = if position.x < border_size {
|
||||
ResizeDirection::West
|
||||
} else if position.x > (win_size.width as f64 - border_size) {
|
||||
ResizeDirection::East
|
||||
} else {
|
||||
// Use arbitrary direction instead of None for simplicity.
|
||||
ResizeDirection::SouthEast
|
||||
};
|
||||
|
||||
let y_direction = if position.y < border_size {
|
||||
ResizeDirection::North
|
||||
} else if position.y > (win_size.height as f64 - border_size) {
|
||||
ResizeDirection::South
|
||||
} else {
|
||||
// Use arbitrary direction instead of None for simplicity.
|
||||
ResizeDirection::SouthEast
|
||||
};
|
||||
|
||||
let direction = match (x_direction, y_direction) {
|
||||
(ResizeDirection::West, ResizeDirection::North) => ResizeDirection::NorthWest,
|
||||
(ResizeDirection::West, ResizeDirection::South) => ResizeDirection::SouthWest,
|
||||
(ResizeDirection::West, _) => ResizeDirection::West,
|
||||
(ResizeDirection::East, ResizeDirection::North) => ResizeDirection::NorthEast,
|
||||
(ResizeDirection::East, ResizeDirection::South) => ResizeDirection::SouthEast,
|
||||
(ResizeDirection::East, _) => ResizeDirection::East,
|
||||
(_, ResizeDirection::South) => ResizeDirection::South,
|
||||
(_, ResizeDirection::North) => ResizeDirection::North,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if let Err(err) = self.window.drag_resize_window(direction) {
|
||||
println!("Error starting window drag-resize: {err}");
|
||||
} else {
|
||||
println!("Drag-resizing window Window={:?}", self.window.id());
|
||||
}
|
||||
}
|
||||
|
||||
/// Change window occlusion state.
|
||||
fn set_occluded(&mut self, occluded: bool) {
|
||||
self.occluded = occluded;
|
||||
if !occluded {
|
||||
self.window.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the window contents.
|
||||
#[cfg(not(any(android_platform, ios_platform)))]
|
||||
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
if self.occluded {
|
||||
println!("Skipping drawing occluded window={:?}", self.window.id());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
const WHITE: u32 = 0xFFFFFFFF;
|
||||
const DARK_GRAY: u32 = 0xFF181818;
|
||||
|
||||
let color = match self.theme {
|
||||
Theme::Light => WHITE,
|
||||
Theme::Dark => DARK_GRAY,
|
||||
};
|
||||
|
||||
let mut buffer = self.surface.buffer_mut()?;
|
||||
buffer.fill(color);
|
||||
self.window.pre_present_notify();
|
||||
buffer.present()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(android_platform, ios_platform))]
|
||||
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
println!("Drawing but without rendering...");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Binding<T: Eq> {
|
||||
trigger: T,
|
||||
mods: ModifiersState,
|
||||
@@ -747,6 +679,7 @@ impl<T: Eq> Binding<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper enum describing the different kinds of actions this example can do.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Action {
|
||||
CloseWindow,
|
||||
|
||||
Reference in New Issue
Block a user