From 2b210c234c32b1ea3d760f018274576d05b24ad6 Mon Sep 17 00:00:00 2001 From: Tiger Huang Date: Mon, 18 Mar 2019 21:21:26 +0800 Subject: [PATCH] Reduce the window tap exclude region for child above it For letting touches directly go to the embedded display, we've opened a tap exclude region for the ActivityView. However, if there is a view on top of the region, the view cannot be touched within the region. In this CL, we reduce the tap exclude region if there is a can-receive- pointer-event view on top of the region. Bug: 128517544 Test: atest CtsActivityManagerDeviceTestCases:ActivityViewTest Test: atest FrameworksCoreTests:ViewGroupTest Test: Menual test with ActivityViewTest and Bubbles Change-Id: I68e2a9fe9d0891801b533ab8d25074f64bef5a79 --- core/java/android/app/ActivityView.java | 49 +++++-- core/java/android/view/IWindowSession.aidl | 9 +- core/java/android/view/View.java | 10 ++ core/java/android/view/ViewGroup.java | 59 ++++++-- core/java/android/view/ViewParent.java | 14 ++ .../src/android/view/ViewGroupTest.java | 133 ++++++++++++++++++ .../animation/PhysicsAnimationLayout.java | 5 + .../android/systemui/statusbar/ScrimView.java | 5 + .../java/com/android/server/wm/Session.java | 5 +- .../server/wm/TapExcludeRegionHolder.java | 35 +++-- .../server/wm/WindowManagerService.java | 11 +- .../com/android/server/wm/WindowState.java | 8 +- 12 files changed, 282 insertions(+), 61 deletions(-) create mode 100644 core/tests/coretests/src/android/view/ViewGroupTest.java diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 2ef085690f8f4..8ec5e3a432186 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -27,6 +27,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Insets; +import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.hardware.input.InputManager; @@ -46,6 +47,7 @@ import android.view.SurfaceSession; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -81,6 +83,9 @@ public class ActivityView extends ViewGroup { // Temp container to store view coordinates in window. private final int[] mLocationInWindow = new int[2]; + // The latest tap exclude region that we've sent to WM. + private final Region mTapExcludeRegion = new Region(); + private TaskStackListener mTaskStackListener; private final CloseGuard mGuard = CloseGuard.get(); @@ -279,11 +284,11 @@ public class ActivityView extends ViewGroup { } /** - * Triggers an update of {@link ActivityView}'s location in window to properly set touch exclude + * Triggers an update of {@link ActivityView}'s location in window to properly set tap exclude * regions and avoid focus switches by touches on this view. */ public void onLocationChanged() { - updateLocation(); + updateTapExcludeRegion(); } @Override @@ -291,15 +296,38 @@ public class ActivityView extends ViewGroup { mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */); } - /** Send current location and size to the WM to set tap exclude region for this view. */ - private void updateLocation() { + @Override + public boolean gatherTransparentRegion(Region region) { + // The tap exclude region may be affected by any view on top of it, so we detect the + // possible change by monitoring this function. + updateTapExcludeRegion(); + return super.gatherTransparentRegion(region); + } + + /** Compute and send current tap exclude region to WM for this view. */ + private void updateTapExcludeRegion() { if (!isAttachedToWindow()) { return; } + if (!canReceivePointerEvents()) { + cleanTapExcludeRegion(); + return; + } try { getLocationInWindow(mLocationInWindow); + final int x = mLocationInWindow[0]; + final int y = mLocationInWindow[1]; + mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight()); + + // There might be views on top of us. We need to subtract those areas from the tap + // exclude region. + final ViewParent parent = getParent(); + if (parent instanceof ViewGroup) { + ((ViewGroup) parent).subtractObscuredTouchableRegion(mTapExcludeRegion, this); + } + WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), - mLocationInWindow[0], mLocationInWindow[1], getWidth(), getHeight()); + mTapExcludeRegion); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } @@ -322,7 +350,7 @@ public class ActivityView extends ViewGroup { mVirtualDisplay.setDisplayState(true); } - updateLocation(); + updateTapExcludeRegion(); } @Override @@ -330,7 +358,7 @@ public class ActivityView extends ViewGroup { if (mVirtualDisplay != null) { mVirtualDisplay.resize(width, height, getBaseDisplayDensity()); } - updateLocation(); + updateTapExcludeRegion(); } @Override @@ -460,13 +488,14 @@ public class ActivityView extends ViewGroup { /** Report to server that tap exclude region on hosting display should be cleared. */ private void cleanTapExcludeRegion() { - if (!isAttachedToWindow()) { + if (!isAttachedToWindow() || mTapExcludeRegion.isEmpty()) { return; } - // Update tap exclude region with an empty rect to clean the state on server. + // Update tap exclude region with a null region to clean the state on server. try { WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), - 0 /* left */, 0 /* top */, 0 /* width */, 0 /* height */); + null /* region */); + mTapExcludeRegion.setEmpty(); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 87efb3fbf6c02..d317df05cc6e0 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -255,12 +255,11 @@ interface IWindowSession { void updatePointerIcon(IWindow window); /** - * Update a tap exclude region with a rectangular area identified by provided id in the window. - * Touches on this region will not switch focus to this window. Passing an empty rect will - * remove the area from the exclude region of this window. + * Update a tap exclude region identified by provided id in the window. Touches on this region + * will neither be dispatched to this window nor change the focus to this window. Passing an + * invalid region will remove the area from the exclude region of this window. */ - void updateTapExcludeRegion(IWindow window, int regionId, int left, int top, int width, - int height); + void updateTapExcludeRegion(IWindow window, int regionId, in Region region); /** * Called when the client has changed the local insets state, and now the server should reflect diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2357db46771a3..2d04796abe3bd 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13898,6 +13898,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, .getAccessibilityFocusedHost() == this); } + /** + * Returns whether this view can receive pointer events. + * + * @return {@code true} if this view can receive pointer events. + * @hide + */ + protected boolean canReceivePointerEvents() { + return (mViewFlags & VISIBILITY_MASK) == VISIBLE || getAnimation() != null; + } + /** * Filter the touch event to apply security policies. * diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 4851476e3d708..937bd1b34e61f 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2011,7 +2011,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); - if (!canViewReceivePointerEvents(child) + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { continue; } @@ -2094,7 +2094,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); - if (!canViewReceivePointerEvents(child) + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { continue; } @@ -2314,7 +2314,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); - if (!canViewReceivePointerEvents(child) + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { continue; } @@ -2500,7 +2500,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); - if (!canViewReceivePointerEvents(child) + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { continue; } @@ -2680,7 +2680,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager i = childrenCount - 1; } - if (!canViewReceivePointerEvents(child) + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; @@ -2970,15 +2970,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - /** - * Returns true if a child view can receive pointer events. - * @hide - */ - private static boolean canViewReceivePointerEvents(@NonNull View child) { - return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE - || child.getAnimation() != null; - } - private float[] getTempPoint() { if (mTempPoint == null) { mTempPoint = new float[2]; @@ -7199,6 +7190,46 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + /** + * @hide + */ + @Override + public void subtractObscuredTouchableRegion(Region touchableRegion, View view) { + final int childrenCount = mChildrenCount; + final ArrayList preorderedList = buildTouchDispatchChildList(); + final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); + final View[] children = mChildren; + for (int i = childrenCount - 1; i >= 0; i--) { + final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); + final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); + if (child == view) { + // We've reached the target view. + break; + } + if (!child.canReceivePointerEvents()) { + // This child cannot be touched. Skip it. + continue; + } + applyOpToRegionByBounds(touchableRegion, child, Region.Op.DIFFERENCE); + } + + // The touchable region should not exceed the bounds of its container. + applyOpToRegionByBounds(touchableRegion, this, Region.Op.INTERSECT); + + final ViewParent parent = getParent(); + if (parent != null) { + parent.subtractObscuredTouchableRegion(touchableRegion, this); + } + } + + private static void applyOpToRegionByBounds(Region region, View view, Region.Op op) { + final int[] locationInWindow = new int[2]; + view.getLocationInWindow(locationInWindow); + final int x = locationInWindow[0]; + final int y = locationInWindow[1]; + region.op(x, y, x + view.getWidth(), y + view.getHeight(), op); + } + @Override public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { insets = super.dispatchApplyWindowInsets(insets); diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index 572e69b1a78c4..feba7bb7f195a 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -18,6 +18,7 @@ package android.view; import android.annotation.NonNull; import android.graphics.Rect; +import android.graphics.Region; import android.os.Bundle; import android.view.accessibility.AccessibilityEvent; @@ -660,4 +661,17 @@ public interface ViewParent { * @return true if the action was consumed by this ViewParent */ public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments); + + /** + * Given a touchable region of a child, this method reduces region by the bounds of all views on + * top of the child for which {@link View#canReceivePointerEvents} returns {@code true}. This + * applies recursively for all views in the view hierarchy on top of this one. + * + * @param touchableRegion The touchable region we want to modify. + * @param view A child view of this ViewGroup which indicates the z-order of the touchable + * region. + * @hide + */ + default void subtractObscuredTouchableRegion(Region touchableRegion, View view) { + } } diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java new file mode 100644 index 0000000000000..979a839d2ffea --- /dev/null +++ b/core/tests/coretests/src/android/view/ViewGroupTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static androidx.test.InstrumentationRegistry.getContext; + +import static org.junit.Assert.assertEquals; + +import android.content.Context; +import android.graphics.Region; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + + +/** + * Test basic functions of ViewGroup. + * + * Build/Install/Run: + * atest FrameworksCoreTests:ViewGroupTest + */ +@Presubmit +@SmallTest +public class ViewGroupTest { + + /** + * Test if {@link ViewGroup#subtractObscuredTouchableRegion} works as expected. + * + * The view hierarchy: + * A---B---C + * \ \ + * \ --D + * \ + * E---F + * + * The layer and bounds of each view: + * F -- (invisible) + * E -- + * D ---- + * C ---------- + * B ------ + * A -------- + */ + @Test + public void testSubtractObscuredTouchableRegion() { + final Context context = getContext(); + final TestView viewA = new TestView(context, 8 /* right */); + final TestView viewB = new TestView(context, 6 /* right */); + final TestView viewC = new TestView(context, 10 /* right */); + final TestView viewD = new TestView(context, 4 /* right */); + final TestView viewE = new TestView(context, 2 /* right */); + final TestView viewF = new TestView(context, 2 /* right */); + + viewA.addView(viewB); + viewA.addView(viewE); + viewB.addView(viewC); + viewB.addView(viewD); + viewE.addView(viewF); + + viewF.setVisibility(View.INVISIBLE); + + final Region r = new Region(); + + getUnobscuredTouchableRegion(r, viewA); + assertRegionContainPoint(1 /* x */, r, true /* contain */); + assertRegionContainPoint(3 /* x */, r, true /* contain */); + assertRegionContainPoint(5 /* x */, r, true /* contain */); + assertRegionContainPoint(7 /* x */, r, true /* contain */); + assertRegionContainPoint(9 /* x */, r, false /* contain */); // Outside of bounds + + getUnobscuredTouchableRegion(r, viewB); + assertRegionContainPoint(1 /* x */, r, false /* contain */); // Obscured by E + assertRegionContainPoint(3 /* x */, r, true /* contain */); + assertRegionContainPoint(5 /* x */, r, true /* contain */); + assertRegionContainPoint(7 /* x */, r, false /* contain */); // Outside of bounds + + getUnobscuredTouchableRegion(r, viewC); + assertRegionContainPoint(1 /* x */, r, false /* contain */); // Obscured by D and E + assertRegionContainPoint(3 /* x */, r, false /* contain */); // Obscured by D + assertRegionContainPoint(5 /* x */, r, true /* contain */); + assertRegionContainPoint(7 /* x */, r, false /* contain */); // Outside of parent bounds + + getUnobscuredTouchableRegion(r, viewD); + assertRegionContainPoint(1 /* x */, r, false /* contain */); // Obscured by E + assertRegionContainPoint(3 /* x */, r, true /* contain */); + assertRegionContainPoint(5 /* x */, r, false /* contain */); // Outside of bounds + + getUnobscuredTouchableRegion(r, viewE); + assertRegionContainPoint(1 /* x */, r, true /* contain */); + assertRegionContainPoint(3 /* x */, r, false /* contain */); // Outside of bounds + } + + private static void getUnobscuredTouchableRegion(Region outRegion, View view) { + outRegion.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); + final ViewParent parent = view.getParent(); + if (parent != null) { + parent.subtractObscuredTouchableRegion(outRegion, view); + } + } + + private static void assertRegionContainPoint(int x, Region region, boolean contain) { + assertEquals(String.format("Touchable region must%s contain (%s, 0).", + (contain ? "" : " not"), x), contain, region.contains(x, 0 /* y */)); + } + + private static class TestView extends ViewGroup { + TestView(Context context, int right) { + super(context); + setFrame(0 /* left */, 0 /* top */, right, 1 /* bottom */); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + // We don't layout this view. + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java index 2fa87d83f75df..599ba1f9a6f55 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java @@ -814,4 +814,9 @@ public class PhysicsAnimationLayout extends FrameLayout { } } } + + @Override + protected boolean canReceivePointerEvents() { + return false; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java index cb9060bc95747..cf6e64cef3657 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java @@ -289,4 +289,9 @@ public class ScrimView extends View implements ConfigurationController.Configura } } } + + @Override + protected boolean canReceivePointerEvents() { + return false; + } } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 9b634f959fcab..22b030dd16200 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -426,11 +426,10 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override - public void updateTapExcludeRegion(IWindow window, int regionId, int left, int top, int width, - int height) { + public void updateTapExcludeRegion(IWindow window, int regionId, Region region) { final long identity = Binder.clearCallingIdentity(); try { - mService.updateTapExcludeRegion(window, regionId, left, top, width, height); + mService.updateTapExcludeRegion(window, regionId, region); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java b/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java index 0a4ab67f033da..22f529b28b8ed 100644 --- a/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java +++ b/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java @@ -21,38 +21,35 @@ import android.graphics.Region; import android.util.SparseArray; /** - * A holder that contains a collection of rectangular areas identified by int id. Each individual - * region can be updated separately. + * A holder that contains a collection of regions identified by int id. Each individual region can + * be updated separately. */ class TapExcludeRegionHolder { - private SparseArray mTapExcludeRects = new SparseArray<>(); + private SparseArray mTapExcludeRegions = new SparseArray<>(); /** Update the specified region with provided position and size. */ - void updateRegion(int regionId, int left, int top, int width, int height) { - if (width <= 0 || height <= 0) { - // A region became empty - remove it. - mTapExcludeRects.remove(regionId); + void updateRegion(int regionId, Region region) { + // Remove the previous one because there is a new one incoming. + mTapExcludeRegions.remove(regionId); + + if (region == null || region.isEmpty()) { + // The incoming region is invalid. Don't use it. return; } - Rect region = mTapExcludeRects.get(regionId); - if (region == null) { - region = new Rect(); - } - region.set(left, top, left + width, top + height); - mTapExcludeRects.put(regionId, region); + mTapExcludeRegions.put(regionId, region); } /** * Union the provided region with current region formed by this container. */ - void amendRegion(Region region, Rect boundingRegion) { - for (int i = mTapExcludeRects.size() - 1; i>= 0 ; --i) { - final Rect rect = mTapExcludeRects.valueAt(i); - if (boundingRegion != null) { - rect.intersect(boundingRegion); + void amendRegion(Region region, Rect bounds) { + for (int i = mTapExcludeRegions.size() - 1; i >= 0; --i) { + final Region r = mTapExcludeRegions.valueAt(i); + if (bounds != null) { + r.op(bounds, Region.Op.INTERSECT); } - region.union(rect); + region.op(r, Region.Op.UNION); } } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4aa844ff4f49d..9e421c1d7db5a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -6696,24 +6696,23 @@ public class WindowManagerService extends IWindowManager.Stub } /** - * Update a tap exclude region with a rectangular area in the window identified by the provided - * id. Touches down on this region will not: + * Update a tap exclude region in the window identified by the provided id. Touches down on this + * region will not: *
    *
  1. Switch focus to this window.
  2. *
  3. Move the display of this window to top.
  4. *
  5. Send the touch events to this window.
  6. *
- * Passing an empty rect will remove the area from the exclude region of this window. + * Passing an invalid region will remove the area from the exclude region of this window. */ - void updateTapExcludeRegion(IWindow client, int regionId, int left, int top, int width, - int height) { + void updateTapExcludeRegion(IWindow client, int regionId, Region region) { synchronized (mGlobalLock) { final WindowState callingWin = windowForClientLocked(null, client, false); if (callingWin == null) { Slog.w(TAG_WM, "Bad requesting window " + client); return; } - callingWin.updateTapExcludeRegion(regionId, left, top, width, height); + callingWin.updateTapExcludeRegion(regionId, region); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 6a21327d8bb9f..93ed87745fb22 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4831,10 +4831,10 @@ class WindowState extends WindowContainer implements WindowManagerP } /** - * Update a tap exclude region with a rectangular area identified by provided id. The requested - * area will be clipped to the window bounds. + * Update a tap exclude region identified by provided id. The requested area will be clipped to + * the window bounds. */ - void updateTapExcludeRegion(int regionId, int left, int top, int width, int height) { + void updateTapExcludeRegion(int regionId, Region region) { final DisplayContent currentDisplay = getDisplayContent(); if (currentDisplay == null) { throw new IllegalStateException("Trying to update window not attached to any display."); @@ -4848,7 +4848,7 @@ class WindowState extends WindowContainer implements WindowManagerP currentDisplay.mTapExcludeProvidingWindows.add(this); } - mTapExcludeRegionHolder.updateRegion(regionId, left, top, width, height); + mTapExcludeRegionHolder.updateRegion(regionId, region); // Trigger touch exclude region update on current display. currentDisplay.updateTouchExcludeRegion(); // Trigger touchable region update for this window.