From fed37f7d3928b0131451d8ba6d2d5ad624ebea57 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 23 Oct 2026 10:33:08 +0300 Subject: [PATCH 1/2] iOS: publish the view size after a modal hides a rotation (#6080) Rotating an iPhone while a full-screen modal that supports only portrait covers the Codename One view (the UIImagePickerController camera) delivers no viewWillTransitionToSize: when the modal is dismissed. The view comes back laid out landscape while the framework keeps the portrait size, so every form draws stretched until the next rotation. viewDidLayoutSubviews now publishes any size the transition callbacks did not, as the Catalyst/tvOS path already does. NativeModalRotationTest reproduces it on the simulator: it presents a portrait-only modal, locks landscape under it, dismisses it and compares the display size with the view's pixel size. Without the fix it fails with "view is 2532x1170 pixels but the display is 1170x2532". Co-Authored-By: Claude Opus 5.5 --- .../CodenameOne_GLViewController.m | 48 +++++ .../NativeModalRotationNativeImpl.java | 50 +++++ .../NativeModalRotationNative.java | 47 +++++ .../tests/Cn1ssDeviceRunner.java | 2 + .../tests/NativeModalRotationTest.java | 188 ++++++++++++++++++ ...odenameone_NativeModalRotationNativeImpl.h | 34 ++++ ...odenameone_NativeModalRotationNativeImpl.m | 153 ++++++++++++++ .../NativeModalRotationNativeImpl.java | 50 +++++ .../NativeModalRotationNativeImpl.cs | 52 +++++ 9 files changed, 624 insertions(+) create mode 100644 scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.java create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNative.java create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/NativeModalRotationTest.java create mode 100644 scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl.h create mode 100644 scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl.m create mode 100644 scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.java create mode 100644 scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.cs diff --git a/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m b/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m index dcd3367e1e8..d1ad2439361 100644 --- a/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m +++ b/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m @@ -3370,6 +3370,54 @@ - (void)viewDidLayoutSubviews { screenSizeChanged(displayWidth, displayHeight); } +#elif !TARGET_OS_OSX && !TARGET_OS_WATCH +// Defined further down, beside the drawing code that reads it. +extern bool lockDrawing; + +// viewWillTransitionToSize: is how an iPhone learns about a rotation, and it +// is not delivered when the interface rotates while a full-screen modal that +// supports only portrait covers this view -- the camera of +// UIImagePickerController is one. The device is turned with the modal up, the +// interface stays portrait, and when the modal is dismissed the view comes back +// laid out landscape with no transition at all. Every form then drew at the +// portrait size stretched across the landscape view until the next rotation +// (issue #6080). Layout always runs with the real bounds, so any size the +// transition callbacks did not publish is published here. +// +// Nothing to debounce, unlike Catalyst above: an iPhone view changes size on a +// rotation or a split-screen resize, not continuously. In the usual case +// viewWillTransitionToSize: has already published the new size and this +// returns at the comparison. The keyboard moves this view's origin, never its +// size, so it does not reach this either. +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + // The same reason viewWillTransitionToSize: gives: iOS lays the view out + // at the opposite orientation for its background snapshot. + if (isAppSuspended || self.view == nil) { + return; + } + CGSize sz = self.view.bounds.size; + int newW = (int)(sz.width * scaleValue); + int newH = (int)(sz.height * scaleValue); + if (newW <= 0 || newH <= 0) { + return; + } + int dw = newW - displayWidth; + int dh = newH - displayHeight; + if (dw < 0) dw = -dw; + if (dh < 0) dh = -dh; + if (dw <= 1 && dh <= 1) { + return; + } + [[self renderingView] updateFrameBufferSize:newW h:newH]; + [[self renderingView] deleteFramebuffer]; + displayWidth = newW; + displayHeight = newH; + lockDrawing = NO; + screenSizeChanged(displayWidth, displayHeight); + repaintUI(); +} + #endif #ifdef INCLUDE_MOPUB diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.java b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.java new file mode 100644 index 00000000000..26d697fbb24 --- /dev/null +++ b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone; + +/// Only iOS presents a native modal over the Codename One view this way. +public class NativeModalRotationNativeImpl { + public boolean presentPortraitOnlyModal() { + return false; + } + + public boolean dismissModal() { + return false; + } + + public boolean isModalPresented() { + return false; + } + + public int getViewPixelWidth() { + return 0; + } + + public int getViewPixelHeight() { + return 0; + } + + public boolean isSupported() { + return false; + } +} diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNative.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNative.java new file mode 100644 index 00000000000..7e601104f93 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNative.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone; + +import com.codename1.system.NativeInterface; + +/// Drives a full-screen native modal that only supports portrait -- the shape +/// of the iOS camera (`UIImagePickerController`) -- so a test can rotate the +/// device while it covers the Codename One view and then dismiss it +/// (issue #6080). Every call returns at once; the work runs on the main +/// thread, and the getters answer the state captured by the previous call. +public interface NativeModalRotationNative extends NativeInterface { + /// Presents the portrait-only modal over the Codename One view. + boolean presentPortraitOnlyModal(); + + /// Dismisses it, animated, as the camera dismisses itself. + boolean dismissModal(); + + /// True while the modal is presented. + boolean isModalPresented(); + + /// The Codename One view's current width in pixels, as UIKit sees it. + int getViewPixelWidth(); + + /// The Codename One view's current height in pixels, as UIKit sees it. + int getViewPixelHeight(); +} diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java index 1806bb39f4f..6b3fa0e8689 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java @@ -518,6 +518,8 @@ private static int testTimeoutMs(BaseTest testClass) { new Gpu3DAnimationTest(), // Keep this as the last portrait screenshot test; orientation changes can leak into subsequent screenshots. new OrientationLockScreenshotTest(), + // Rotates under a native modal and restores portrait itself (#6080). + new NativeModalRotationTest(), new InPlaceEditViewTest(), new BytecodeTranslatorRegressionTest(), new SimdApiTest(), diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/NativeModalRotationTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/NativeModalRotationTest.java new file mode 100644 index 00000000000..9b015c58000 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/NativeModalRotationTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone.tests; + +import com.codename1.system.NativeLookup; +import com.codename1.ui.CN; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.util.UITimer; +import com.codenameone.examples.hellocodenameone.NativeModalRotationNative; + +/// Issue #6080: rotate the device while a full-screen native modal that only +/// supports portrait covers the Codename One view -- the iOS camera is one -- +/// and dismiss it. The interface rotates to landscape as the modal goes away, +/// and the framework has to learn the new size then; when it did not, every +/// form drew with the portrait width stretched across the landscape view until +/// the device was rotated again. +/// +/// Asserts that once the modal is gone and the view is landscape, the display +/// size the framework uses equals the view's size in pixels. iOS only; other +/// platforms have no such modal and finish at once. +/// +/// No screenshot -- this is an assertion test. +public class NativeModalRotationTest extends BaseTest { + private static final int POLL_INTERVAL_MS = 100; + // The modal is presented without animation, the dismissal is animated and + // the rotation follows it; 5s is far beyond either on a starved runner. + private static final int POLL_ATTEMPTS = 50; + // Lets a rotation that the modal failed to hold back land before checking. + private static final int UNDER_MODAL_SETTLE_MS = 1500; + private static final int TOLERANCE_PX = 2; + + private NativeModalRotationNative modal; + + @Override + public boolean shouldTakeScreenshot() { + return false; + } + + @Override + public boolean runTest() { + modal = NativeLookup.create(NativeModalRotationNative.class); + if (modal == null || !modal.isSupported() || !CN.canForceOrientation()) { + done(); + return true; + } + Form form = new Form("Native Modal Rotation", new BoxLayout(BoxLayout.Y_AXIS)) { + @Override + protected void onShowCompleted() { + start(this); + } + }; + form.add(new Label("Rotating under a native modal...")); + form.show(); + return true; + } + + private void start(final Form form) { + CN.lockOrientation(true); + poll(form, POLL_ATTEMPTS, new Condition() { + public boolean holds() { + return modal.getViewPixelHeight() > modal.getViewPixelWidth(); + } + }, "the device to start in portrait", new Runnable() { + public void run() { + modal.presentPortraitOnlyModal(); + poll(form, POLL_ATTEMPTS, new Condition() { + public boolean holds() { + return modal.isModalPresented(); + } + }, "the native modal to be presented", new Runnable() { + public void run() { + rotateUnderModal(form); + } + }); + } + }); + } + + private void rotateUnderModal(final Form form) { + // The device turns to landscape; the modal keeps the interface + // portrait, exactly as the camera does. + CN.lockOrientation(false); + UITimer.timer(UNDER_MODAL_SETTLE_MS, false, form, new Runnable() { + public void run() { + // Portrait here is what makes this the camera's path: a modal + // that let the interface rotate would test an ordinary rotation. + System.out.println("CN1SS:INFO:test=NativeModalRotationTest under the modal view=" + + modal.getViewPixelWidth() + "x" + modal.getViewPixelHeight() + + " display=" + Display.getInstance().getDisplayWidth() + + "x" + Display.getInstance().getDisplayHeight()); + modal.dismissModal(); + poll(form, POLL_ATTEMPTS, new Condition() { + public boolean holds() { + return !modal.isModalPresented() + && modal.getViewPixelWidth() > modal.getViewPixelHeight(); + } + }, "the modal to close and the view to turn landscape", new Runnable() { + public void run() { + awaitDisplayMatchesView(form, POLL_ATTEMPTS); + } + }); + } + }); + } + + /// Polls rather than checking once: the size reaches the EDT as an event, + /// so a correct port may need a frame or two. A port that never delivers it + /// keeps the portrait size for the whole budget. + private void awaitDisplayMatchesView(final Form form, final int attemptsLeft) { + int viewW = modal.getViewPixelWidth(); + int viewH = modal.getViewPixelHeight(); + int displayW = Display.getInstance().getDisplayWidth(); + int displayH = Display.getInstance().getDisplayHeight(); + if (Math.abs(viewW - displayW) <= TOLERANCE_PX && Math.abs(viewH - displayH) <= TOLERANCE_PX) { + finish(form, null); + return; + } + if (attemptsLeft <= 0) { + finish(form, "after a native modal closed over a rotation the view is " + + viewW + "x" + viewH + " pixels but the display is " + + displayW + "x" + displayH + " (issue #6080)"); + return; + } + UITimer.timer(POLL_INTERVAL_MS, false, form, new Runnable() { + public void run() { + awaitDisplayMatchesView(form, attemptsLeft - 1); + } + }); + } + + private void finish(final Form form, final String failure) { + modal.dismissModal(); + LandscapeCapture.restorePortrait(form, new Runnable() { + public void run() { + if (failure != null) { + fail(failure); + } else { + done(); + } + } + }); + } + + private interface Condition { + boolean holds(); + } + + private void poll(final Form form, final int attemptsLeft, final Condition condition, + final String what, final Runnable then) { + if (condition.holds()) { + then.run(); + return; + } + if (attemptsLeft <= 0) { + finish(form, "timed out waiting for " + what + "; view=" + + modal.getViewPixelWidth() + "x" + modal.getViewPixelHeight()); + return; + } + UITimer.timer(POLL_INTERVAL_MS, false, form, new Runnable() { + public void run() { + poll(form, attemptsLeft - 1, condition, what, then); + } + }); + } +} diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl.h b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl.h new file mode 100644 index 00000000000..8c410fe31e5 --- /dev/null +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +#import + +@interface com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl : NSObject { +} + +-(BOOL)presentPortraitOnlyModal; +-(BOOL)dismissModal; +-(BOOL)isModalPresented; +-(int)getViewPixelWidth; +-(int)getViewPixelHeight; +-(BOOL)isSupported; +@end diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl.m b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl.m new file mode 100644 index 00000000000..a51a9aaf65f --- /dev/null +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl.m @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +#import "com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl.h" + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#import "CodenameOne_GLViewController.h" +#import + +extern float scaleValue; + +// Stands in for UIImagePickerController's camera, which keeps its interface +// portrait however the phone is held (issue #6080). +@interface CN1PortraitOnlyModalController : UIViewController +@end + +@implementation CN1PortraitOnlyModalController +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskPortrait; +} +- (BOOL)shouldAutorotate { + return NO; +} +- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { + return UIInterfaceOrientationPortrait; +} +@end + +// Written on the main thread only, read from the EDT. A stale read is one poll +// behind, which the test's polling absorbs. +static UIViewController* modalController = nil; +static int modalPresented = 0; +static int viewPixelWidth = 0; +static int viewPixelHeight = 0; + +static void snapshotState() { + dispatch_async(dispatch_get_main_queue(), ^{ + UIView* v = [CodenameOne_GLViewController instance].view; + viewPixelWidth = (int)(v.bounds.size.width * scaleValue); + viewPixelHeight = (int)(v.bounds.size.height * scaleValue); + modalPresented = (modalController != nil && modalController.presentingViewController != nil) ? 1 : 0; + }); +} + +@implementation com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl + +-(BOOL)presentPortraitOnlyModal { + dispatch_async(dispatch_get_main_queue(), ^{ + if (modalController != nil) { + return; + } + CN1PortraitOnlyModalController* c = [[CN1PortraitOnlyModalController alloc] init]; + c.view.backgroundColor = [UIColor blackColor]; + c.modalPresentationStyle = UIModalPresentationFullScreen; + modalController = c; + [[CodenameOne_GLViewController instance] presentViewController:c animated:NO completion:nil]; + }); + snapshotState(); + return YES; +} + +-(BOOL)dismissModal { + dispatch_async(dispatch_get_main_queue(), ^{ + UIViewController* c = modalController; + if (c == nil) { + return; + } + [c dismissViewControllerAnimated:YES completion:^{ + if (modalController == c) { +#ifndef CN1_USE_ARC + [modalController release]; +#endif + modalController = nil; + } + }]; + }); + snapshotState(); + return YES; +} + +-(BOOL)isModalPresented { + snapshotState(); + return modalPresented != 0; +} + +-(int)getViewPixelWidth { + snapshotState(); + return viewPixelWidth; +} + +-(int)getViewPixelHeight { + snapshotState(); + return viewPixelHeight; +} + +-(BOOL)isSupported { + return YES; +} + +@end + +#else + +// The watch, tv and Catalyst slices compile this file too; none of them has +// interface orientation to rotate under a modal. +@implementation com_codenameone_examples_hellocodenameone_NativeModalRotationNativeImpl + +-(BOOL)presentPortraitOnlyModal { + return NO; +} + +-(BOOL)dismissModal { + return NO; +} + +-(BOOL)isModalPresented { + return NO; +} + +-(int)getViewPixelWidth { + return 0; +} + +-(int)getViewPixelHeight { + return 0; +} + +-(BOOL)isSupported { + return NO; +} + +@end + +#endif diff --git a/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.java b/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.java new file mode 100644 index 00000000000..26d697fbb24 --- /dev/null +++ b/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone; + +/// Only iOS presents a native modal over the Codename One view this way. +public class NativeModalRotationNativeImpl { + public boolean presentPortraitOnlyModal() { + return false; + } + + public boolean dismissModal() { + return false; + } + + public boolean isModalPresented() { + return false; + } + + public int getViewPixelWidth() { + return 0; + } + + public int getViewPixelHeight() { + return 0; + } + + public boolean isSupported() { + return false; + } +} diff --git a/scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.cs b/scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.cs new file mode 100644 index 00000000000..0718f498a4a --- /dev/null +++ b/scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/NativeModalRotationNativeImpl.cs @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +namespace com.codenameone.examples.hellocodenameone{ + + +public class NativeModalRotationNativeImpl : INativeModalRotationNativeImpl { + public bool presentPortraitOnlyModal() { + return false; + } + + public bool dismissModal() { + return false; + } + + public bool isModalPresented() { + return false; + } + + public int getViewPixelWidth() { + return 0; + } + + public int getViewPixelHeight() { + return 0; + } + + public bool isSupported() { + return false; + } + +} +} From 8908cf026ef4822d14740ce743a78c6d4e923d29 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 23 Oct 2026 18:36:10 +0300 Subject: [PATCH 2/2] Port status: register NativeModalRotationTest under orientation, scoped to iOS Co-Authored-By: Claude Opus 5.5 --- docs/website/data/port_status.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/website/data/port_status.json b/docs/website/data/port_status.json index 75d68e0a811..8a78cd53145 100644 --- a/docs/website/data/port_status.json +++ b/docs/website/data/port_status.json @@ -573,8 +573,9 @@ "id": "orientation", "category": "Input and lifecycle", "name": "Orientation locking", - "description": "Checks orientation changes and the rendered landscape state.", + "description": "Checks orientation changes, the rendered landscape state, and a rotation hidden by a native modal.", "tests": [ + "NativeModalRotationTest", "OrientationLockScreenshotTest" ] }, @@ -911,6 +912,9 @@ } ], "test_scopes": { + "NativeModalRotationTest": [ + "ios-metal" + ], "SystemBackNavigationTest": [ "android" ],