Skip to content

Commit

Permalink
Position winit view within safe area
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmerlin committed Feb 28, 2024
1 parent 6abd9d5 commit 10bd20e
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 50 deletions.
53 changes: 50 additions & 3 deletions src/platform_impl/ios/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::cell::Cell;
use std::ptr::NonNull;

use icrate::Foundation::{
CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet, NSString,
CGFloat, CGPoint, CGRect, CGSize, MainThreadMarker, NSObject, NSObjectProtocol, NSSet, NSString,
};
use objc2::declare::{Ivar, IvarDrop};
use objc2::rc::Id;
Expand Down Expand Up @@ -84,7 +84,7 @@ declare_class!(
let _: () = unsafe { msg_send![super(self), layoutSubviews] };

let window = self.window().unwrap();
let window_bounds = window.bounds();
let window_bounds = unsafe { self.safe_area_screen_space() };
let screen = window.screen();
let screen_space = screen.coordinateSpace();
let screen_frame = self.convertRect_toCoordinateSpace(window_bounds, &screen_space);
Expand Down Expand Up @@ -243,6 +243,53 @@ impl WinitView {
this
}

// requires main thread
pub(crate) unsafe fn safe_area_screen_space(&self) -> CGRect {
let screen = self.window().unwrap().screen();
let screen_space = screen.coordinateSpace();
let bounds = self.bounds();
if app_state::os_capabilities().safe_area {
let safe_area = self.safeAreaInsets();
let safe_bounds = CGRect {
origin: CGPoint {
x: bounds.origin.x + safe_area.left,
y: bounds.origin.y + safe_area.top,
},
size: CGSize {
width: bounds.size.width - safe_area.left - safe_area.right,
height: bounds.size.height - safe_area.top - safe_area.bottom,
},
};
self.convertRect_toCoordinateSpace(safe_bounds, &screen_space)
} else {
let screen_frame = self.convertRect_toCoordinateSpace(bounds, &screen_space);
let status_bar_frame = {
let app = UIApplication::shared(MainThreadMarker::new().unwrap()).expect(
"`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS",
);
app.statusBarFrame()
};
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {
(screen_frame.origin.y, screen_frame.size.height)
} else {
let y = status_bar_frame.size.height;
let height = screen_frame.size.height
- (status_bar_frame.size.height - screen_frame.origin.y);
(y, height)
};
CGRect {
origin: CGPoint {
x: screen_frame.origin.x,
y,
},
size: CGSize {
width: screen_frame.size.width,
height,
},
}
}
}

fn handle_insert_text(&self, text: &NSString) {
let window = self.window().unwrap();
let uiscreen = window.screen();
Expand Down Expand Up @@ -314,7 +361,7 @@ impl WinitView {
let mut touch_events = Vec::new();
let os_supports_force = app_state::os_capabilities().force_touch;
for touch in touches {
let logical_location = touch.locationInView(None);
let logical_location = touch.locationInView(Some(&self));
let touch_type = touch.type_();
let force = if os_supports_force {
let trait_collection = unsafe { self.traitCollection() };
Expand Down
54 changes: 7 additions & 47 deletions src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use objc2::{class, msg_send};
use super::app_state::EventWrapper;
use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
use super::view::{WinitUIWindow, WinitView, WinitViewController};
use crate::event::TextInputState;
use crate::{
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
Expand All @@ -24,7 +25,6 @@ use crate::{
WindowAttributes, WindowButtons, WindowId as RootWindowId, WindowLevel,
},
};
use crate::event::TextInputState;

pub struct Inner {
window: Id<WinitUIWindow>,
Expand Down Expand Up @@ -74,8 +74,12 @@ impl Inner {

pub fn pre_present_notify(&self) {}

pub fn safe_area(&self) -> CGRect {
unsafe { self.view.safe_area_screen_space() }
}

pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let safe_area = self.safe_area_screen_space();
let safe_area = self.safe_area();
let position = LogicalPosition {
x: safe_area.origin.x as f64,
y: safe_area.origin.y as f64,
Expand Down Expand Up @@ -111,7 +115,7 @@ impl Inner {

pub fn inner_size(&self) -> PhysicalSize<u32> {
let scale_factor = self.scale_factor();
let safe_area = self.safe_area_screen_space();
let safe_area = self.safe_area();
let size = LogicalSize {
width: safe_area.size.width as f64,
height: safe_area.size.height as f64,
Expand Down Expand Up @@ -585,50 +589,6 @@ impl Inner {
self.window
.convertRect_fromCoordinateSpace(rect, &screen_space)
}

fn safe_area_screen_space(&self) -> CGRect {
let bounds = self.window.bounds();
if app_state::os_capabilities().safe_area {
let safe_area = self.window.safeAreaInsets();
let safe_bounds = CGRect {
origin: CGPoint {
x: bounds.origin.x + safe_area.left,
y: bounds.origin.y + safe_area.top,
},
size: CGSize {
width: bounds.size.width - safe_area.left - safe_area.right,
height: bounds.size.height - safe_area.top - safe_area.bottom,
},
};
self.rect_to_screen_space(safe_bounds)
} else {
let screen_frame = self.rect_to_screen_space(bounds);
let status_bar_frame = {
let app = UIApplication::shared(MainThreadMarker::new().unwrap()).expect(
"`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS",
);
app.statusBarFrame()
};
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {
(screen_frame.origin.y, screen_frame.size.height)
} else {
let y = status_bar_frame.size.height;
let height = screen_frame.size.height
- (status_bar_frame.size.height - screen_frame.origin.y);
(y, height)
};
CGRect {
origin: CGPoint {
x: screen_frame.origin.x,
y,
},
size: CGSize {
width: screen_frame.size.width,
height,
},
}
}
}
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand Down

0 comments on commit 10bd20e

Please sign in to comment.