diff --git a/CodenameOne/src/com/codename1/ui/Sheet.java b/CodenameOne/src/com/codename1/ui/Sheet.java index 69cc9b045f..93b7697014 100644 --- a/CodenameOne/src/com/codename1/ui/Sheet.java +++ b/CodenameOne/src/com/codename1/ui/Sheet.java @@ -113,6 +113,7 @@ /// /// 7.0 public class Sheet extends Container { + private static Rectangle[] sheetBoundsList = new Rectangle[0]; private static final int N = 0; private static final int S = 1; private static final int E = 2; @@ -175,6 +176,70 @@ public void actionPerformed(ActionEvent evt) { /// These are set the first time the sheet is shown and used as the base for safe area calculations. private int[] originalPadding = null; private Form form; + private final Rectangle sheetBounds = new Rectangle(); + private boolean trackSheetBounds; + private Rectangle sheetEntry; + + public static boolean isSheetVisibleAt(int x, int y) { + Rectangle[] boundsSnapshot = sheetBoundsList; + for (Rectangle bounds : boundsSnapshot) { + if (bounds.contains(x, y)) { + return true; + } + } + return false; + } + + private static void addSheetEntry(Rectangle bounds) { + Rectangle[] current = sheetBoundsList; + Rectangle[] updated = new Rectangle[current.length + 1]; + System.arraycopy(current, 0, updated, 0, current.length); + updated[current.length] = bounds; + sheetBoundsList = updated; + } + + private static void removeSheetEntry(Rectangle bounds) { + Rectangle[] current = sheetBoundsList; + int matches = 0; + for (Rectangle existing : current) { + if (existing == bounds) { // NOPMD CompareObjectsWithEquals + matches++; + } + } + if (matches == 0) { + return; + } + Rectangle[] updated = new Rectangle[current.length - matches]; + int index = 0; + for (Rectangle existing : current) { + if (existing != bounds) { // NOPMD CompareObjectsWithEquals + updated[index++] = existing; + } + } + sheetBoundsList = updated; + } + + private void updateTrackedBounds() { + if (!trackSheetBounds) { + return; + } + sheetBounds.setBounds(getAbsoluteX(), getAbsoluteY(), getWidth(), getHeight()); + } + + private void startTrackingBounds() { + trackSheetBounds = true; + sheetEntry = sheetBounds; + addSheetEntry(sheetEntry); + updateTrackedBounds(); + } + + private void stopTrackingBounds() { + trackSheetBounds = false; + if (sheetEntry != null) { + removeSheetEntry(sheetEntry); + sheetEntry = null; + } + } /// Creates a new sheet with the specified parent and title. /// @@ -466,6 +531,7 @@ public void run() { cnt.revalidate(); } + startTrackingBounds(); if (cnt.getComponentCount() > 0) { $(".Sheet", cnt).each(new ComponentClosure() { @Override @@ -503,6 +569,9 @@ public void call(Component c) { }); Component existing = cnt.getComponentAt(0); + if (existing instanceof Sheet) { + ((Sheet) existing).stopTrackingBounds(); + } cnt.replace(existing, this, null); cnt.animateLayout(duration); } else { @@ -799,6 +868,7 @@ public void run() { cnt.remove(); parent.getComponentForm().revalidateLater(); fireCloseEvent(true); + stopTrackingBounds(); } @@ -808,6 +878,30 @@ public void run() { } + @Override + public void setX(int x) { + super.setX(x); + updateTrackedBounds(); + } + + @Override + public void setY(int y) { + super.setY(y); + updateTrackedBounds(); + } + + @Override + public void setWidth(int width) { + super.setWidth(width); + updateTrackedBounds(); + } + + @Override + public void setHeight(int height) { + super.setHeight(height); + updateTrackedBounds(); + } + /// Gets the parent sheet or null if there is none. /// /// #### Returns diff --git a/Ports/Android/src/com/codename1/impl/android/CodenameOneView.java b/Ports/Android/src/com/codename1/impl/android/CodenameOneView.java index 83180c26a0..8e89e7b479 100644 --- a/Ports/Android/src/com/codename1/impl/android/CodenameOneView.java +++ b/Ports/Android/src/com/codename1/impl/android/CodenameOneView.java @@ -31,11 +31,12 @@ import android.view.*; import android.view.inputmethod.EditorInfo; import android.os.Build; -import com.codename1.ui.Component; -import com.codename1.ui.Display; -import com.codename1.ui.Form; -import com.codename1.ui.PeerComponent; -import com.codename1.ui.TextArea; +import com.codename1.ui.Component; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.PeerComponent; +import com.codename1.ui.Sheet; +import com.codename1.ui.TextArea; import com.codename1.ui.events.ActionEvent; import com.codename1.ui.events.ActionListener; import java.lang.reflect.Method; @@ -561,20 +562,25 @@ public boolean onTouchEvent(MotionEvent event) { //if (nativePeerGrabbedPointer) { // return false; //} - Component componentAt; - try { - if (x == null) { - componentAt = this.implementation.getCurrentForm().getComponentAt((int)event.getX(), (int)event.getY()); - } else { - componentAt = this.implementation.getCurrentForm().getComponentAt((int)x[0], (int)y[0]); - } - } catch (Throwable t) { - // Since this is is an EDT violation, we may get an exception - // Just consume it - componentAt = null; - } - boolean isPeer = (componentAt instanceof PeerComponent); - boolean consumeEvent = !isPeer || cn1GrabbedPointer; + Component componentAt; + try { + if (x == null) { + componentAt = this.implementation.getCurrentForm().getComponentAt((int)event.getX(), (int)event.getY()); + } else { + componentAt = this.implementation.getCurrentForm().getComponentAt((int)x[0], (int)y[0]); + } + } catch (Throwable t) { + // Since this is is an EDT violation, we may get an exception + // Just consume it + componentAt = null; + } + boolean isPeer = (componentAt instanceof PeerComponent); + if (isPeer) { + int primaryX = x == null ? (int) event.getX() : x[0]; + int primaryY = y == null ? (int) event.getY() : y[0]; + isPeer = !Sheet.isSheetVisibleAt(primaryX, primaryY); + } + boolean consumeEvent = !isPeer || cn1GrabbedPointer; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: diff --git a/Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java b/Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java index aa29b3195c..41bfe398e6 100644 --- a/Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java +++ b/Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java @@ -36,6 +36,7 @@ import com.codename1.ui.Font; import com.codename1.ui.Image; import com.codename1.ui.PeerComponent; +import com.codename1.ui.Sheet; import com.codename1.ui.TextArea; import com.codename1.ui.TextField; import com.codename1.ui.geom.Dimension; @@ -1157,7 +1158,10 @@ static boolean hitTest(int x, int y) { Form f = Display.getInstance().getCurrent(); if (f != null) { Component cmp = f.getResponderAt(x, y); - return cmp == null || !(cmp instanceof PeerComponent); + if (cmp == null || !(cmp instanceof PeerComponent)) { + return true; + } + return Sheet.isSheetVisibleAt(x, y); } return true; } diff --git a/scripts/android/screenshots/Sheet.png b/scripts/android/screenshots/Sheet.png new file mode 100644 index 0000000000..5878c2eb4e Binary files /dev/null and b/scripts/android/screenshots/Sheet.png differ 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 30e80f34a9..fd10adeaea 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 @@ -69,6 +69,7 @@ public final class Cn1ssDeviceRunner extends DeviceRunner { new BrowserComponentScreenshotTest(), new MediaPlaybackScreenshotTest(), new OrientationLockScreenshotTest(), + new SheetScreenshotTest(), new InPlaceEditViewTest(), new BytecodeTranslatorRegressionTest(), new BackgroundThreadUiAccessTest(), diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/SheetScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/SheetScreenshotTest.java new file mode 100644 index 0000000000..f8f1506488 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/SheetScreenshotTest.java @@ -0,0 +1,46 @@ +package com.codenameone.examples.hellocodenameone.tests; + +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.Sheet; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.util.UITimer; + +public class SheetScreenshotTest extends BaseTest { + private Form form; + private Sheet sheet; + private Sheet childSheet; + + @Override + public boolean runTest() { + form = createForm("Sheet Test", new BorderLayout(), "Sheet"); + form.add(BorderLayout.CENTER, new Label("Tap sheet to dismiss")); + sheet = createSheet(null, "Sheet"); + form.show(); + return true; + } + + @Override + protected void registerReadyCallback(Form parent, Runnable run) { + sheet.show(); + UITimer.timer(500, false, parent, () -> { + childSheet = createSheet(sheet, "Details"); + childSheet.show(); + UITimer.timer(600, false, parent, run); + }); + } + + private Sheet createSheet(Sheet parent, String title) { + Sheet newSheet = new Sheet(parent, title); + Container content = newSheet.getContentPane(); + content.setLayout(BoxLayout.y()); + content.add(new Label("Sheet content")); + content.add(new Button("Primary Action")); + content.add(new Label("Secondary details")); + newSheet.getCommandsContainer().add(new Button("Edit")); + return newSheet; + } +} diff --git a/scripts/ios/screenshots/Sheet.png b/scripts/ios/screenshots/Sheet.png new file mode 100644 index 0000000000..8ad0e74a26 Binary files /dev/null and b/scripts/ios/screenshots/Sheet.png differ