1
0
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:
Konkitoman
2023-08-15 02:17:12 +03:00
159 changed files with 2536 additions and 1163 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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>,

View File

@@ -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

View File

@@ -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);
}