mirror of
https://github.com/emilk/egui.git
synced 2026-09-02 06:40:06 -04:00
Merge branch 'master' of https://github.com/emilk/egui into multiples_viewports
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# Changelog for egui-winit
|
||||
All notable changes to the `egui-winit` integration will be noted in this file.
|
||||
|
||||
|
||||
## Unreleased
|
||||
This file is updated upon each release.
|
||||
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||
|
||||
|
||||
## 0.22.0 - 2023-05-23
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.22.0"
|
||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||
description = "Bindings for using egui with winit"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
rust-version = "1.67"
|
||||
homepage = "https://github.com/emilk/egui/tree/master/crates/egui-winit"
|
||||
license = "MIT OR Apache-2.0"
|
||||
readme = "README.md"
|
||||
@@ -18,7 +18,7 @@ all-features = true
|
||||
|
||||
|
||||
[features]
|
||||
default = ["clipboard", "links", "wayland", "winit/default"]
|
||||
default = ["clipboard", "links", "wayland", "winit/default", "x11"]
|
||||
|
||||
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
|
||||
accesskit = ["accesskit_winit", "egui/accesskit"]
|
||||
@@ -42,6 +42,9 @@ serde = ["egui/serde", "dep:serde"]
|
||||
## Enables Wayland support.
|
||||
wayland = ["winit/wayland"]
|
||||
|
||||
## Enables compiling for x11.
|
||||
x11 = ["winit/x11"]
|
||||
|
||||
# Allow crates to choose an android-activity backend via Winit
|
||||
# - It's important that most applications should not have to depend on android-activity directly, and can
|
||||
# rely on Winit to pull in a suitable version (unlike most Rust crates, any version conflicts won't link)
|
||||
|
||||
@@ -3,7 +3,7 @@ use raw_window_handle::HasRawDisplayHandle;
|
||||
/// Handles interfacing with the OS clipboard.
|
||||
///
|
||||
/// If the "clipboard" feature is off, or we cannot connect to the OS clipboard,
|
||||
/// then a fallback clipboard that just works works within the same app is used instead.
|
||||
/// then a fallback clipboard that just works within the same app is used instead.
|
||||
pub struct Clipboard {
|
||||
#[cfg(all(feature = "arboard", not(target_os = "android")))]
|
||||
arboard: Option<arboard::Clipboard>,
|
||||
|
||||
@@ -313,6 +313,7 @@ impl State {
|
||||
}
|
||||
WindowEvent::KeyboardInput { input, .. } => {
|
||||
self.on_keyboard_input(input);
|
||||
// When pressing the Tab key, egui focuses the first focusable element, hence Tab always consumes.
|
||||
let consumed = egui_ctx.wants_keyboard_input()
|
||||
|| input.virtual_keycode == Some(winit::event::VirtualKeyCode::Tab);
|
||||
EventResponse {
|
||||
@@ -454,7 +455,7 @@ impl State {
|
||||
id: egui::TouchId(0),
|
||||
phase: egui::TouchPhase::Start,
|
||||
pos,
|
||||
force: 0.0,
|
||||
force: None,
|
||||
});
|
||||
} else {
|
||||
self.any_pointer_button_down = false;
|
||||
@@ -466,7 +467,7 @@ impl State {
|
||||
id: egui::TouchId(0),
|
||||
phase: egui::TouchPhase::End,
|
||||
pos,
|
||||
force: 0.0,
|
||||
force: None,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -492,7 +493,7 @@ impl State {
|
||||
id: egui::TouchId(0),
|
||||
phase: egui::TouchPhase::Move,
|
||||
pos: pos_in_points,
|
||||
force: 0.0,
|
||||
force: None,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -518,13 +519,13 @@ impl State {
|
||||
touch.location.y as f32 / self.pixels_per_point(),
|
||||
),
|
||||
force: match touch.force {
|
||||
Some(winit::event::Force::Normalized(force)) => force as f32,
|
||||
Some(winit::event::Force::Normalized(force)) => Some(force as f32),
|
||||
Some(winit::event::Force::Calibrated {
|
||||
force,
|
||||
max_possible_force,
|
||||
..
|
||||
}) => (force / max_possible_force) as f32,
|
||||
None => 0_f32,
|
||||
}) => Some((force / max_possible_force) as f32),
|
||||
None => None,
|
||||
},
|
||||
});
|
||||
// If we're not yet translating a touch or we're translating this very
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use egui::ViewportBuilder;
|
||||
|
||||
/// Can be used to store native window settings (position and size).
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct WindowSettings {
|
||||
/// Position of window in physical pixels. This is either
|
||||
/// the inner or outer position depending on the platform.
|
||||
/// See [`winit::window::WindowBuilder::with_position`] for details.
|
||||
position: Option<egui::Pos2>,
|
||||
/// Position of window content in physical pixels.
|
||||
inner_position_pixels: Option<egui::Pos2>,
|
||||
|
||||
/// Position of window frame/titlebar in physical pixels.
|
||||
outer_position_pixels: Option<egui::Pos2>,
|
||||
|
||||
fullscreen: bool,
|
||||
|
||||
@@ -18,22 +20,20 @@ pub struct WindowSettings {
|
||||
impl WindowSettings {
|
||||
pub fn from_display(window: &winit::window::Window) -> Self {
|
||||
let inner_size_points = window.inner_size().to_logical::<f32>(window.scale_factor());
|
||||
let position = if cfg!(macos) {
|
||||
// MacOS uses inner position when positioning windows.
|
||||
window
|
||||
.inner_position()
|
||||
.ok()
|
||||
.map(|p| egui::pos2(p.x as f32, p.y as f32))
|
||||
} else {
|
||||
// Other platforms use the outer position.
|
||||
window
|
||||
.outer_position()
|
||||
.ok()
|
||||
.map(|p| egui::pos2(p.x as f32, p.y as f32))
|
||||
};
|
||||
|
||||
let inner_position_pixels = window
|
||||
.inner_position()
|
||||
.ok()
|
||||
.map(|p| egui::pos2(p.x as f32, p.y as f32));
|
||||
|
||||
let outer_position_pixels = window
|
||||
.outer_position()
|
||||
.ok()
|
||||
.map(|p| egui::pos2(p.x as f32, p.y as f32));
|
||||
|
||||
Self {
|
||||
position,
|
||||
inner_position_pixels,
|
||||
outer_position_pixels,
|
||||
|
||||
fullscreen: window.fullscreen().is_some(),
|
||||
|
||||
@@ -48,13 +48,15 @@ impl WindowSettings {
|
||||
self.inner_size_points
|
||||
}
|
||||
|
||||
pub fn initialize_window(&self, mut window: ViewportBuilder) -> ViewportBuilder {
|
||||
// If the app last ran on two monitors and only one is now connected, then
|
||||
// the given position is invalid.
|
||||
// If this happens on Mac, the window is clamped into valid area.
|
||||
// If this happens on Windows, the clamping behavior is managed by the function
|
||||
// clamp_window_to_sane_position.
|
||||
if let Some(pos) = self.position {
|
||||
pub fn initialize_window_builder(&self, mut window: ViewportBuilder) -> ViewportBuilder {
|
||||
// `WindowBuilder::with_position` expects inner position in Macos, and outer position elsewhere
|
||||
// See [`winit::window::WindowBuilder::with_position`] for details.
|
||||
let pos_px = if cfg!(target_os = "macos") {
|
||||
self.inner_position_pixels
|
||||
} else {
|
||||
self.outer_position_pixels
|
||||
};
|
||||
if let Some(pos) = pos_px {
|
||||
window = window.with_position(Some((pos.x as i32, pos.y as i32)));
|
||||
}
|
||||
|
||||
@@ -70,68 +72,103 @@ impl WindowSettings {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clamp_to_sane_values(&mut self, max_size: egui::Vec2) {
|
||||
pub fn initialize_window(&self, window: &winit::window::Window) {
|
||||
if cfg!(target_os = "macos") {
|
||||
// Mac sometimes has problems restoring the window to secondary monitors
|
||||
// using only `WindowBuilder::with_position`, so we need this extra step:
|
||||
if let Some(pos) = self.outer_position_pixels {
|
||||
window.set_outer_position(winit::dpi::PhysicalPosition { x: pos.x, y: pos.y });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clamp_size_to_sane_values(&mut self, largest_monitor_size_points: egui::Vec2) {
|
||||
use egui::NumExt as _;
|
||||
|
||||
if let Some(size) = &mut self.inner_size_points {
|
||||
// Prevent ridiculously small windows
|
||||
// Prevent ridiculously small windows:
|
||||
let min_size = egui::Vec2::splat(64.0);
|
||||
*size = size.at_least(min_size);
|
||||
*size = size.at_most(max_size);
|
||||
|
||||
// Make sure we don't try to create a window larger than the largest monitor
|
||||
// because on Linux that can lead to a crash.
|
||||
*size = size.at_most(largest_monitor_size_points);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clamp_window_to_sane_position<E>(
|
||||
pub fn clamp_position_to_monitors<E>(
|
||||
&mut self,
|
||||
event_loop: &winit::event_loop::EventLoopWindowTarget<E>,
|
||||
) {
|
||||
if let (Some(position), Some(inner_size_points)) =
|
||||
(&mut self.position, &self.inner_size_points)
|
||||
{
|
||||
let monitors = event_loop.available_monitors();
|
||||
// default to primary monitor, in case the correct monitor was disconnected.
|
||||
let mut active_monitor = if let Some(active_monitor) = event_loop
|
||||
.primary_monitor()
|
||||
.or_else(|| event_loop.available_monitors().next())
|
||||
{
|
||||
active_monitor
|
||||
} else {
|
||||
return; // no monitors 🤷
|
||||
};
|
||||
for monitor in monitors {
|
||||
let monitor_x_range = (monitor.position().x - inner_size_points.x as i32)
|
||||
..(monitor.position().x + monitor.size().width as i32);
|
||||
let monitor_y_range = (monitor.position().y - inner_size_points.y as i32)
|
||||
..(monitor.position().y + monitor.size().height as i32);
|
||||
// If the app last ran on two monitors and only one is now connected, then
|
||||
// the given position is invalid.
|
||||
// If this happens on Mac, the window is clamped into valid area.
|
||||
// If this happens on Windows, the window becomes invisible to the user 🤦♂️
|
||||
// So on Windows we clamp the position to the monitor it is on.
|
||||
if !cfg!(target_os = "windows") {
|
||||
return;
|
||||
}
|
||||
|
||||
if monitor_x_range.contains(&(position.x as i32))
|
||||
&& monitor_y_range.contains(&(position.y as i32))
|
||||
{
|
||||
active_monitor = monitor;
|
||||
}
|
||||
}
|
||||
let Some(inner_size_points) = self.inner_size_points else { return; };
|
||||
|
||||
let mut inner_size_pixels = *inner_size_points * (active_monitor.scale_factor() as f32);
|
||||
// Add size of title bar. This is 32 px by default in Win 10/11.
|
||||
if cfg!(target_os = "windows") {
|
||||
inner_size_pixels +=
|
||||
egui::Vec2::new(0.0, 32.0 * active_monitor.scale_factor() as f32);
|
||||
}
|
||||
let monitor_position = egui::Pos2::new(
|
||||
active_monitor.position().x as f32,
|
||||
active_monitor.position().y as f32,
|
||||
);
|
||||
let monitor_size = egui::Vec2::new(
|
||||
active_monitor.size().width as f32,
|
||||
active_monitor.size().height as f32,
|
||||
);
|
||||
|
||||
// Window size cannot be negative or the subsequent `clamp` will panic.
|
||||
let window_size = (monitor_size - inner_size_pixels).max(egui::Vec2::ZERO);
|
||||
// To get the maximum position, we get the rightmost corner of the display, then
|
||||
// subtract the size of the window to get the bottom right most value window.position
|
||||
// can have.
|
||||
*position = position.clamp(monitor_position, monitor_position + window_size);
|
||||
if let Some(pos_px) = &mut self.inner_position_pixels {
|
||||
clamp_pos_to_monitors(event_loop, inner_size_points, pos_px);
|
||||
}
|
||||
if let Some(pos_px) = &mut self.outer_position_pixels {
|
||||
clamp_pos_to_monitors(event_loop, inner_size_points, pos_px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clamp_pos_to_monitors<E>(
|
||||
event_loop: &winit::event_loop::EventLoopWindowTarget<E>,
|
||||
window_size_pts: egui::Vec2,
|
||||
position_px: &mut egui::Pos2,
|
||||
) {
|
||||
let monitors = event_loop.available_monitors();
|
||||
|
||||
// default to primary monitor, in case the correct monitor was disconnected.
|
||||
let mut active_monitor = if let Some(active_monitor) = event_loop
|
||||
.primary_monitor()
|
||||
.or_else(|| event_loop.available_monitors().next())
|
||||
{
|
||||
active_monitor
|
||||
} else {
|
||||
return; // no monitors 🤷
|
||||
};
|
||||
|
||||
for monitor in monitors {
|
||||
let window_size_px = window_size_pts * (monitor.scale_factor() as f32);
|
||||
let monitor_x_range = (monitor.position().x - window_size_px.x as i32)
|
||||
..(monitor.position().x + monitor.size().width as i32);
|
||||
let monitor_y_range = (monitor.position().y - window_size_px.y as i32)
|
||||
..(monitor.position().y + monitor.size().height as i32);
|
||||
|
||||
if monitor_x_range.contains(&(position_px.x as i32))
|
||||
&& monitor_y_range.contains(&(position_px.y as i32))
|
||||
{
|
||||
active_monitor = monitor;
|
||||
}
|
||||
}
|
||||
|
||||
let mut window_size_px = window_size_pts * (active_monitor.scale_factor() as f32);
|
||||
// Add size of title bar. This is 32 px by default in Win 10/11.
|
||||
if cfg!(target_os = "windows") {
|
||||
window_size_px += egui::Vec2::new(0.0, 32.0 * active_monitor.scale_factor() as f32);
|
||||
}
|
||||
let monitor_position = egui::Pos2::new(
|
||||
active_monitor.position().x as f32,
|
||||
active_monitor.position().y as f32,
|
||||
);
|
||||
let monitor_size_px = egui::Vec2::new(
|
||||
active_monitor.size().width as f32,
|
||||
active_monitor.size().height as f32,
|
||||
);
|
||||
|
||||
// Window size cannot be negative or the subsequent `clamp` will panic.
|
||||
let window_size = (monitor_size_px - window_size_px).max(egui::Vec2::ZERO);
|
||||
// To get the maximum position, we get the rightmost corner of the display, then
|
||||
// subtract the size of the window to get the bottom right most value window.position
|
||||
// can have.
|
||||
*position_px = position_px.clamp(monitor_position, monitor_position + window_size);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user