|
| 1 | +// Copyright © SixtyFPS GmbH <info@slint.dev> |
| 2 | +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 |
| 3 | + |
| 4 | +// Stopgap until winit ships iOS appearance support (rust-windowing/winit#4570). |
| 5 | +// Once that lands, `winit_window.theme()` and `WindowEvent::ThemeChanged` will |
| 6 | +// work on iOS and this whole module — plus the `ios_color_scheme_observer` |
| 7 | +// wiring in winitwindowadapter.rs — can be deleted. |
| 8 | + |
| 9 | +use std::ptr::NonNull; |
| 10 | +use std::rc::Weak; |
| 11 | + |
| 12 | +use block2::RcBlock; |
| 13 | +use objc2::{ |
| 14 | + ClassType, Message, available, msg_send, |
| 15 | + rc::Retained, |
| 16 | + runtime::{AnyClass, ProtocolObject}, |
| 17 | +}; |
| 18 | +use objc2_foundation::NSArray; |
| 19 | +use objc2_ui_kit::{ |
| 20 | + UITraitChangeObservable, UITraitChangeRegistration, UITraitCollection, UITraitEnvironment, |
| 21 | + UITraitUserInterfaceStyle, UIUserInterfaceStyle, UIView, |
| 22 | +}; |
| 23 | + |
| 24 | +use i_slint_core::items::ColorScheme; |
| 25 | + |
| 26 | +use crate::winitwindowadapter::WinitWindowAdapter; |
| 27 | + |
| 28 | +fn style_to_color_scheme(style: UIUserInterfaceStyle) -> ColorScheme { |
| 29 | + match style { |
| 30 | + UIUserInterfaceStyle::Dark => ColorScheme::Dark, |
| 31 | + UIUserInterfaceStyle::Light => ColorScheme::Light, |
| 32 | + _ => ColorScheme::Unknown, |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +pub(crate) fn current_color_scheme(view: &UIView) -> ColorScheme { |
| 37 | + style_to_color_scheme(unsafe { view.traitCollection().userInterfaceStyle() }) |
| 38 | +} |
| 39 | + |
| 40 | +pub(crate) struct ColorSchemeObserver { |
| 41 | + view: Retained<UIView>, |
| 42 | + registration: Retained<ProtocolObject<dyn UITraitChangeRegistration>>, |
| 43 | +} |
| 44 | + |
| 45 | +impl Drop for ColorSchemeObserver { |
| 46 | + fn drop(&mut self) { |
| 47 | + self.view.unregisterForTraitChanges(&self.registration); |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +pub(crate) fn install_color_scheme_observer( |
| 52 | + view: &UIView, |
| 53 | + adapter: Weak<WinitWindowAdapter>, |
| 54 | +) -> Option<ColorSchemeObserver> { |
| 55 | + // `registerForTraitChanges:withHandler:` is iOS 17+. Older iOS still gets the |
| 56 | + // initial scheme via `current_color_scheme`, but no live updates. |
| 57 | + if !available!(ios = 17.0) { |
| 58 | + return None; |
| 59 | + } |
| 60 | + |
| 61 | + let handler = RcBlock::new( |
| 62 | + move |env: NonNull<ProtocolObject<dyn UITraitEnvironment>>, |
| 63 | + _prev: NonNull<UITraitCollection>| { |
| 64 | + let Some(adapter) = adapter.upgrade() else { return }; |
| 65 | + let env = unsafe { env.as_ref() }; |
| 66 | + let scheme = |
| 67 | + style_to_color_scheme(unsafe { env.traitCollection().userInterfaceStyle() }); |
| 68 | + adapter.set_color_scheme(scheme); |
| 69 | + }, |
| 70 | + ); |
| 71 | + |
| 72 | + let traits: Retained<NSArray<AnyClass>> = |
| 73 | + NSArray::from_slice(&[UITraitUserInterfaceStyle::class()]); |
| 74 | + |
| 75 | + let registration: Retained<ProtocolObject<dyn UITraitChangeRegistration>> = unsafe { |
| 76 | + msg_send![ |
| 77 | + view, |
| 78 | + registerForTraitChanges: &*traits, |
| 79 | + withHandler: &*handler, |
| 80 | + ] |
| 81 | + }; |
| 82 | + |
| 83 | + Some(ColorSchemeObserver { view: view.retain(), registration }) |
| 84 | +} |
0 commit comments