Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion winit-core/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,8 @@ pub enum WindowEvent {
///
/// ## Platform-specific
///
/// - **iOS / Android / X11 / Wayland / Orbital:** Unsupported.
/// - **Android / X11 / Wayland / Orbital:** Unsupported.
/// - **iOS:** Requires iOS 17.0+.
ThemeChanged(Theme),

/// The window has been occluded (completely hidden from view).
Expand Down
6 changes: 4 additions & 2 deletions winit-core/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1267,7 +1267,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug {
/// get the system preference.
/// - **X11:** Sets `_GTK_THEME_VARIANT` hint to `dark` or `light` and if `None` is used, it
/// will default to [`Theme::Dark`].
/// - **iOS / Android / Web / Orbital:** Unsupported.
/// - **Android / Web / Orbital:** Unsupported.
/// - **iOS:** Requires iOS 13.0+.
fn set_theme(&self, theme: Option<Theme>);

/// Returns the current window theme.
Expand All @@ -1276,7 +1277,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug {
///
/// ## Platform-specific
///
/// - **iOS / Android / x11 / Orbital:** Unsupported.
/// - **Android / x11 / Orbital:** Unsupported.
/// - **iOS:** Requires iOS 13.0+.
/// - **Wayland:** Only returns theme overrides.
fn theme(&self) -> Option<Theme>;

Expand Down
2 changes: 2 additions & 0 deletions winit-uikit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ objc2-ui-kit = { workspace = true, features = [
"UIEvent",
"UIGeometry",
"UIGestureRecognizer",
"UIInterface",
"UITextInput",
"UITextInputTraits",
"UIOrientation",
Expand All @@ -64,6 +65,7 @@ objc2-ui-kit = { workspace = true, features = [
"UIScreenMode",
"UITapGestureRecognizer",
"UITouch",
"UITrait",
"UITraitCollection",
"UIView",
"UIViewController",
Expand Down
46 changes: 42 additions & 4 deletions winit-uikit/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ use std::cell::{Cell, RefCell};

use dpi::PhysicalPosition;
use objc2::rc::Retained;
use objc2::runtime::{NSObjectProtocol, ProtocolObject};
use objc2::{DefinedClass, MainThreadMarker, available, define_class, msg_send, sel};
use objc2::runtime::{AnyObject, NSObjectProtocol, ProtocolObject};
use objc2::{ClassType, DefinedClass, MainThreadMarker, available, define_class, msg_send, sel};
use objc2_core_foundation::{CGFloat, CGPoint, CGRect};
use objc2_foundation::{NSObject, NSSet, NSString};
use objc2_foundation::{NSArray, NSObject, NSSet, NSString};
use objc2_ui_kit::{
UIEvent, UIForceTouchCapability, UIGestureRecognizer, UIGestureRecognizerDelegate,
UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer, UIPinchGestureRecognizer,
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITextInputTraits, UITouch,
UITouchPhase, UITouchType, UITraitEnvironment, UIView,
UITouchPhase, UITouchType, UITraitEnvironment, UITraitUserInterfaceStyle, UIUserInterfaceStyle,
UIView,
};
use tracing::{debug, debug_span, trace_span};
use winit_core::event::{
Expand All @@ -22,6 +23,7 @@ use winit_core::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, P

use super::app_state::{self, EventWrapper};
use super::window::WinitUIWindow;
use crate::window::ui_style_to_theme;

pub struct WinitViewState {
pinch_gesture_recognizer: RefCell<Option<Retained<UIPinchGestureRecognizer>>>,
Expand Down Expand Up @@ -133,6 +135,28 @@ define_class!(
self.setNeedsDisplay();
}

#[unsafe(method(winitDidChangeUserInterfaceStyle:))]
fn winit_did_change_user_interface_style(&self, _environment: &AnyObject) {
let _entered = debug_span!("winitDidChangeUserInterfaceStyle:").entered();

let Some(window) = self.window() else { return };

// Mirror AppKit's semantics: don't emit when the user has set an override; the change
// was driven by `Window::set_theme`, not by the system.
if window.overrideUserInterfaceStyle() != UIUserInterfaceStyle::Unspecified {
return;
}

let style = unsafe { self.traitCollection().userInterfaceStyle() };
let Some(new_theme) = ui_style_to_theme(style) else { return };

let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, EventWrapper::Window {
window_id: window.id(),
event: WindowEvent::ThemeChanged(new_theme),
});
}

#[unsafe(method(touchesBegan:withEvent:))]
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
let _entered = debug_span!("touchesBegan:withEvent:").entered();
Expand Down Expand Up @@ -380,6 +404,20 @@ impl WinitView {
this.setContentScaleFactor(scale_factor as _);
}

if available!(ios = 17.0, tvos = 17.0, visionos = 1.0) {
let trait_class: &AnyObject =
<UITraitUserInterfaceStyle as ClassType>::class().as_ref();
let traits = NSArray::from_slice(&[trait_class]);
let _: Retained<AnyObject> = unsafe {
msg_send![
&*this,
registerForTraitChanges: &*traits,
withTarget: &*this,
action: sel!(winitDidChangeUserInterfaceStyle:),
]
};
}

this
}

Expand Down
42 changes: 37 additions & 5 deletions winit-uikit/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use objc2_core_foundation::{CGFloat, CGPoint, CGRect, CGSize};
use objc2_foundation::{NSObject, NSObjectProtocol};
use objc2_ui_kit::{
UIApplication, UICoordinateSpace, UIEdgeInsets, UIResponder, UIScreen,
UIScreenOverscanCompensation, UIViewController, UIWindow,
UIScreenOverscanCompensation, UITraitEnvironment, UIUserInterfaceStyle, UIViewController,
UIWindow,
};
use tracing::{debug, debug_span, warn};
use winit_core::cursor::Cursor;
Expand Down Expand Up @@ -455,8 +456,12 @@ impl Inner {
}

pub fn theme(&self) -> Option<Theme> {
warn!("`Window::theme` is ignored on iOS");
None
if available!(ios = 13.0, tvos = 13.0, visionos = 1.0) {
let trait_collection = self.view.traitCollection();
ui_style_to_theme(unsafe { trait_collection.userInterfaceStyle() })
} else {
None
}
}

pub fn set_content_protected(&self, _protected: bool) {}
Expand All @@ -466,8 +471,12 @@ impl Inner {
}

#[inline]
pub fn set_theme(&self, _theme: Option<Theme>) {
warn!("`Window::set_theme` is ignored on iOS");
pub fn set_theme(&self, theme: Option<Theme>) {
if available!(ios = 13.0, tvos = 13.0, visionos = 1.0) {
self.window.setOverrideUserInterfaceStyle(theme_to_ui_style(theme));
} else {
warn!("`Window::set_theme` requires iOS 13.0+");
}
}

pub fn title(&self) -> String {
Expand Down Expand Up @@ -540,6 +549,13 @@ impl Window {

let view_controller = WinitViewController::new(mtm, &ios_attributes, &view);
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);

if let Some(preferred_theme) = window_attributes.preferred_theme {
if available!(ios = 13.0, tvos = 13.0, visionos = 1.0) {
window.setOverrideUserInterfaceStyle(theme_to_ui_style(Some(preferred_theme)));
}
}

window.makeKeyAndVisible();

let inner = Inner {
Expand Down Expand Up @@ -911,3 +927,19 @@ impl Inner {
self.window.convertRect_fromCoordinateSpace(rect, &screen_space)
}
}

pub(crate) fn ui_style_to_theme(style: UIUserInterfaceStyle) -> Option<Theme> {
match style {
UIUserInterfaceStyle::Light => Some(Theme::Light),
UIUserInterfaceStyle::Dark => Some(Theme::Dark),
_ => None,
}
}

pub(crate) fn theme_to_ui_style(theme: Option<Theme>) -> UIUserInterfaceStyle {
match theme {
Some(Theme::Light) => UIUserInterfaceStyle::Light,
Some(Theme::Dark) => UIUserInterfaceStyle::Dark,
None => UIUserInterfaceStyle::Unspecified,
}
}
1 change: 1 addition & 0 deletions winit/src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ changelog entry.

- Add `keyboard` support for OpenHarmony.
- On iOS, add Apple Pencil support with force, altitude, and azimuth data.
- On iOS, implement `Window::theme`, `Window::set_theme`, and `WindowAttributes::with_theme` (iOS 13.0+), and `WindowEvent::ThemeChanged` (iOS 17.0+).
- On Redox, add support for missing keyboard scancodes.
- Implement `Send` and `Sync` for `OwnedDisplayHandle`.
- Use new macOS 15 cursors for resize icons.
Expand Down
Loading