65#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS)
73 [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillChangeStatusBarFrameNotification
74 object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *) {
75 for (QWindow *window : QGuiApplication::allWindows())
76 QWindowSystemInterface::handleSafeAreaMarginsChanged<QWindowSystemInterface::AsynchronousDelivery>(window);
86 return [CAEAGLLayer class];
88 return [
super layerClass];
91- (instancetype)initWithQIOSWindow:(QT_PREPEND_NAMESPACE(
QIOSWindow) *)window
93 if (self = [self initWithFrame:
window->geometry().toCGRect()]) {
98 action:@selector(handleScroll:)];
104 if (@available(ios 13.4, *)) {
110 [
self addGestureRecognizer:m_scrollGestureRecognizer];
112 if ([
self.layer isKindOfClass:CAMetalLayer.class]) {
114 if (
QColorSpace colorSpace =
window->format().colorSpace(); colorSpace.isValid()) {
115 QCFType<CFDataRef> iccData = colorSpace.iccProfile().toCFData();
116 QCFType<CGColorSpaceRef> cgColorSpace = CGColorSpaceCreateWithICCData(iccData);
117 CAMetalLayer *metalLayer =
static_cast<CAMetalLayer *
>(
self.layer);
118 metalLayer.colorspace = cgColorSpace;
119 qCDebug(lcQpaWindow) <<
"Set" <<
self <<
"color space to" << metalLayer.colorspace;
127- (instancetype)initWithFrame:(CGRect)frame
129 if ((self = [super initWithFrame:
frame])) {
131 if ([
self.layer isKindOfClass:[CAEAGLLayer class]]) {
133 CAEAGLLayer *eaglLayer =
static_cast<CAEAGLLayer *
>(
self.layer);
134 eaglLayer.opaque = TRUE;
135 eaglLayer.drawableProperties = @{
136 kEAGLDrawablePropertyRetainedBacking: @(YES),
137 kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8
146 self.multipleTouchEnabled = YES;
152 for (
CGFloat diff = 0; diff < 0.1 || diff > 0.9; diff = fabs(hue - lastHue))
155 #define colorWithBrightness(br) \
156 [UIColor colorWithHue:hue saturation:0.5 brightness:br alpha:1.0].CGColor
159 self.layer.borderWidth = 1.0;
163 UIView *safeAreaOverlay = [[UIView alloc] initWithFrame:CGRectZero];
164 [safeAreaOverlay setBackgroundColor:[UIColor colorWithRed:0.3 green:0.7 blue:0.9 alpha:0.3]];
165 [
self addSubview:safeAreaOverlay];
167 safeAreaOverlay.translatesAutoresizingMaskIntoConstraints = NO;
168 [safeAreaOverlay.topAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.topAnchor].active = YES;
169 [safeAreaOverlay.leftAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.leftAnchor].active = YES;
170 [safeAreaOverlay.rightAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.rightAnchor].active = YES;
171 [safeAreaOverlay.bottomAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor].active = YES;
180 [m_accessibleElements release];
181 [m_scrollGestureRecognizer release];
186- (NSString *)description
188 NSMutableString *description = [NSMutableString stringWithString:[
super description]];
190#ifndef QT_NO_DEBUG_STREAM
191 QString platformWindowDescription;
193 debug.nospace() <<
"; " <<
self.platformWindow <<
">";
194 NSRange lastCharacter = [description rangeOfComposedCharacterSequenceAtIndex:description.length - 1];
195 [description replaceCharactersInRange:lastCharacter withString:platformWindowDescription.toNSString()];
201#if !defined(Q_OS_VISIONOS)
202- (
void)willMoveToWindow:(UIWindow *)newWindow
206 self.contentScaleFactor = newWindow && newWindow.screen ?
207 newWindow.screen.scale : [[UIScreen mainScreen] scale];
213- (
void)didAddSubview:(UIView *)subview
215 if ([subview isKindOfClass:[
QUIView class]])
216 self.clipsToBounds = YES;
219- (
void)willRemoveSubview:(UIView *)subview
226 self.clipsToBounds = NO;
229- (
void)setNeedsDisplay
231 [
super setNeedsDisplay];
235 [
self.layer setNeedsDisplay];
238- (
void)layoutSubviews
246 if (!CGAffineTransformIsIdentity(
self.transform))
247 qWarning() <<
self <<
"has a transform set. This is not supported.";
251 QRect currentGeometry = QRectF::fromCGRect(
self.frame).toRect();
252 qCDebug(lcQpaWindow) <<
self.platformWindow <<
"new geometry is" << currentGeometry;
255 if (currentGeometry.size() != lastReportedGeometry.size()) {
257 [
self setNeedsDisplay];
264- (
void)displayLayer:(CALayer *)layer
269 if (!
self.platformWindow)
272 [
self sendUpdatedExposeEvent];
275- (
void)sendUpdatedExposeEvent
279 if (
self.platformWindow->isExposed()) {
280 QSize bounds = QRectF::fromCGRect(
self.layer.bounds).toRect().size();
288 qCDebug(lcQpaWindow) <<
self.platformWindow << region <<
"isExposed" <<
self.platformWindow->isExposed();
292- (
void)safeAreaInsetsDidChange
299- (BOOL)canBecomeFirstResponder
305- (BOOL)becomeFirstResponder
313 qImDebug() <<
"self:" <<
self <<
"first:" << [UIResponder qt_currentFirstResponder];
315 if (![super becomeFirstResponder]) {
316 qImDebug() <<
self <<
"was not allowed to become first responder";
323 if (
qGuiApp->focusWindow() !=
self.platformWindow->window())
326 qImDebug() <<
self.platformWindow->window() <<
"already active, not sending window activation";
331- (BOOL)responderShouldTriggerWindowDeactivation:(UIResponder *)responder
335 if ([responder isKindOfClass:[
QUIView class]])
341 while ((responder = [responder nextResponder])) {
342 if ([responder isKindOfClass:[
QUIView class]])
350- (BOOL)resignFirstResponder
352 qImDebug() <<
"self:" <<
self <<
"first:" << [UIResponder qt_currentFirstResponder];
354 if (![super resignFirstResponder])
360 if ([self responderShouldTriggerWindowDeactivation:newResponder])
366- (BOOL)isActiveWindow
372 if ([self isFirstResponder])
375 UIResponder *firstResponder = [UIResponder qt_currentFirstResponder];
377 && [firstResponder nextResponder] ==
self)
385- (
void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
387 [
super traitCollectionDidChange: previousTraitCollection];
390 QPointingDevice::Capabilities touchCapabilities = touchDevice->
capabilities();
393 (
self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable));
395 touchDevice->setCapabilities(touchCapabilities);
398-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
402 return [
super pointInside:point withEvent:event];
405- (
void)handleTouches:(NSSet *)touches withEvent:(UIEvent *)event withState:(
QEventPoint::
State)state withTimestamp:(
ulong)timeStamp
410#if QT_CONFIG(tabletevent)
412 NSArray<UITouch *> *cTouches = [event coalescedTouchesForTouch:m_activePencilTouch];
414 for (UITouch *cTouch
in cTouches) {
415 QPointF localViewPosition = QPointF::fromCGPoint([cTouch preciseLocationInView:
self]);
416 QPoint localViewPositionI = localViewPosition.toPoint();
417 QPointF globalScreenPosition =
self.platformWindow->mapToGlobal(localViewPositionI) +
418 (localViewPosition - localViewPositionI);
419 qreal pressure = cTouch.force / cTouch.maximumPossibleForce;
421 CGVector azimuth = [cTouch azimuthUnitVectorInView: self];
427 qCDebug(lcQpaTablet) <<
i <<
":" << timeStamp << localViewPosition << pressure <<
state <<
"azimuth" << azimuth.dx << azimuth.dy
428 <<
"angle" << azimuthAngle <<
"altitude" << cTouch.altitudeAngle
429 <<
"xTilt" <<
qBound(-60.0, altitudeAngle * azimuth.dx, 60.0) <<
"yTilt" <<
qBound(-60.0, altitudeAngle * azimuth.dy, 60.0);
434 pressure,
qBound(-60.0, altitudeAngle * azimuth.dx, 60.0),
qBound(-60.0, altitudeAngle * azimuth.dy, 60.0),
442 if (m_activeTouches.isEmpty())
444 for (
auto it = m_activeTouches.
begin();
it != m_activeTouches.
end(); ++
it) {
447 UITouch *uiTouch = nil;
448 for (UITouch *touch
in touches) {
449 if (touch.hash ==
hash) {
463 QPoint localViewPosition = QPointF::fromCGPoint([uiTouch locationInView:
self]).toPoint();
464 QPoint globalScreenPosition =
self.platformWindow->mapToGlobal(localViewPosition);
469 QSize screenSize =
self.platformWindow->screen()->geometry().
size();
471 globalScreenPosition.y() / screenSize.
height());
473 if (supportsPressure) {
478 touchPoint.
pressure = uiTouch.force / uiTouch.maximumPossibleForce;
497 QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::AsynchronousDelivery>(
498 self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values());
503 QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::AsynchronousDelivery>(
504 self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values());
508- (
void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
514 for (UITouch *touch
in touches) {
515#if QT_CONFIG(tabletevent)
516 if (touch.type == UITouchTypeStylus) {
518 qWarning(
"ignoring additional Pencil while first is still active");
524 Q_ASSERT(!m_activeTouches.contains(touch.hash));
528 static quint16 nextTouchId = 0;
529 m_activeTouches[touch.hash].id = nextTouchId++;
530#if QT_CONFIG(tabletevent)
535 if (
self.platformWindow->shouldAutoActivateWindow() && m_activeTouches.
size() == 1) {
543 [
self handleTouches:touches withEvent:event withState:QEventPoint::State::Pressed withTimestamp:getTimeStamp(event)];
546- (
void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
548 [
self handleTouches:touches withEvent:event withState:QEventPoint::State::Updated withTimestamp:getTimeStamp(event)];
551- (
void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
553 [
self handleTouches:touches withEvent:event withState:QEventPoint::State::Released withTimestamp:getTimeStamp(event)];
557 for (UITouch *touch
in touches) {
558#if QT_CONFIG(tabletevent)
559 if (touch.type == UITouchTypeStylus) {
564 m_activeTouches.remove(touch.hash);
569 m_activeTouches.clear();
573- (
void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
600 qWarning(
"Subset of active touches cancelled by UIKit");
602 m_activeTouches.clear();
605 ulong timestamp =
event ? getTimeStamp(
event) : ([[NSProcessInfo processInfo] systemUptime] * 1000);
612 QWindowSystemInterface::handleTouchCancelEvent<QWindowSystemInterface::AsynchronousDelivery>(
613 self.platformWindow->window(), timestamp, iosIntegration->touchDevice());
616- (int)mapPressTypeToKey:(UIPress*)press withModifiers:(
Qt::KeyboardModifiers)qtModifiers text:(
QString &)text
618 switch (press.type) {
627 if (@available(ios 13.4, *)) {
628 NSString *charactersIgnoringModifiers = press.key.charactersIgnoringModifiers;
633 charactersIgnoringModifiers,
text);
638- (bool)isControlKey:(
Qt::
Key)key
653- (bool)handlePresses:(NSSet<UIPress *> *)presses eventType:(
QEvent::
Type)type
659 if (!
qApp->focusWindow())
662 bool eventHandled =
false;
665 for (UIPress* press
in presses) {
667 if (@available(ios 13.4, *))
670 int key = [
self mapPressTypeToKey:press withModifiers:qtModifiers text:text];
673 if (imEnabled && ![self isControlKey:
Qt::
Key(
key)])
678 eventHandled = eventHandled || keyHandled;