nsresult PuppetWidget::ConfigureChildren(const nsTArray<Configuration>& aConfigurations) { for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { const Configuration& configuration = aConfigurations[i]; PuppetWidget* w = static_cast<PuppetWidget*>(configuration.mChild.get()); NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); w->SetWindowClipRegion(configuration.mClipRegion, true); LayoutDeviceIntRect bounds = w->GetBounds(); if (bounds.Size() != configuration.mBounds.Size()) { w->Resize(configuration.mBounds.x, configuration.mBounds.y, configuration.mBounds.width, configuration.mBounds.height, true); } else if (bounds.TopLeft() != configuration.mBounds.TopLeft()) { w->Move(configuration.mBounds.x, configuration.mBounds.y); } w->SetWindowClipRegion(configuration.mClipRegion, false); } return NS_OK; }
nsresult nsResizerFrame::HandleEvent(nsPresContext* aPresContext, WidgetGUIEvent* aEvent, nsEventStatus* aEventStatus) { NS_ENSURE_ARG_POINTER(aEventStatus); if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { return NS_OK; } nsWeakFrame weakFrame(this); bool doDefault = true; switch (aEvent->mMessage) { case eTouchStart: case eMouseDown: { if (aEvent->mClass == eTouchEventClass || (aEvent->mClass == eMouseEventClass && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton)) { nsCOMPtr<nsIBaseWindow> window; nsIPresShell* presShell = aPresContext->GetPresShell(); nsIContent* contentToResize = GetContentToResize(presShell, getter_AddRefs(window)); if (contentToResize) { nsIFrame* frameToResize = contentToResize->GetPrimaryFrame(); if (!frameToResize) break; // cache the content rectangle for the frame to resize // GetScreenRectInAppUnits returns the border box rectangle, so // adjust to get the desired content rectangle. nsRect rect = frameToResize->GetScreenRectInAppUnits(); switch (frameToResize->StylePosition()->mBoxSizing) { case StyleBoxSizing::Content: rect.Deflate(frameToResize->GetUsedPadding()); MOZ_FALLTHROUGH; case StyleBoxSizing::Padding: rect.Deflate(frameToResize->GetUsedBorder()); MOZ_FALLTHROUGH; case StyleBoxSizing::Border: // nothing break; } mMouseDownRect = LayoutDeviceIntRect::FromAppUnitsToNearest(rect, aPresContext->AppUnitsPerDevPixel()); doDefault = false; } else { // If there is no window, then resizing isn't allowed. if (!window) break; doDefault = false; // ask the widget implementation to begin a resize drag if it can Direction direction = GetDirection(); nsresult rv = aEvent->mWidget->BeginResizeDrag(aEvent, direction.mHorizontal, direction.mVertical); // for native drags, don't set the fields below if (rv != NS_ERROR_NOT_IMPLEMENTED) break; // if there's no native resize support, we need to do window // resizing ourselves window->GetPositionAndSize(&mMouseDownRect.x, &mMouseDownRect.y, &mMouseDownRect.width, &mMouseDownRect.height); } // remember current mouse coordinates LayoutDeviceIntPoint refPoint; if (!GetEventPoint(aEvent, refPoint)) return NS_OK; mMouseDownPoint = refPoint + aEvent->mWidget->WidgetToScreenOffset(); // we're tracking mTrackingMouseMove = true; nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED); } } break; case eTouchEnd: case eMouseUp: { if (aEvent->mClass == eTouchEventClass || (aEvent->mClass == eMouseEventClass && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton)) { // we're done tracking. mTrackingMouseMove = false; nsIPresShell::SetCapturingContent(nullptr, 0); doDefault = false; } } break; case eTouchMove: case eMouseMove: { if (mTrackingMouseMove) { nsCOMPtr<nsIBaseWindow> window; nsIPresShell* presShell = aPresContext->GetPresShell(); nsCOMPtr<nsIContent> contentToResize = GetContentToResize(presShell, getter_AddRefs(window)); // check if the returned content really is a menupopup nsMenuPopupFrame* menuPopupFrame = nullptr; if (contentToResize) { menuPopupFrame = do_QueryFrame(contentToResize->GetPrimaryFrame()); } // both MouseMove and direction are negative when pointing to the // top and left, and positive when pointing to the bottom and right // retrieve the offset of the mousemove event relative to the mousedown. // The difference is how much the resize needs to be LayoutDeviceIntPoint refPoint; if (!GetEventPoint(aEvent, refPoint)) return NS_OK; LayoutDeviceIntPoint screenPoint = refPoint + aEvent->mWidget->WidgetToScreenOffset(); LayoutDeviceIntPoint mouseMove(screenPoint - mMouseDownPoint); // Determine which direction to resize by checking the dir attribute. // For windows and menus, ensure that it can be resized in that direction. Direction direction = GetDirection(); if (window || menuPopupFrame) { if (menuPopupFrame) { menuPopupFrame->CanAdjustEdges( (direction.mHorizontal == -1) ? NS_SIDE_LEFT : NS_SIDE_RIGHT, (direction.mVertical == -1) ? NS_SIDE_TOP : NS_SIDE_BOTTOM, mouseMove); } } else if (!contentToResize) { break; // don't do anything if there's nothing to resize } LayoutDeviceIntRect rect = mMouseDownRect; // Check if there are any size constraints on this window. widget::SizeConstraints sizeConstraints; if (window) { nsCOMPtr<nsIWidget> widget; window->GetMainWidget(getter_AddRefs(widget)); sizeConstraints = widget->GetSizeConstraints(); } AdjustDimensions(&rect.x, &rect.width, sizeConstraints.mMinSize.width, sizeConstraints.mMaxSize.width, mouseMove.x, direction.mHorizontal); AdjustDimensions(&rect.y, &rect.height, sizeConstraints.mMinSize.height, sizeConstraints.mMaxSize.height, mouseMove.y, direction.mVertical); // Don't allow resizing a window or a popup past the edge of the screen, // so adjust the rectangle to fit within the available screen area. if (window) { nsCOMPtr<nsIScreen> screen; nsCOMPtr<nsIScreenManager> sm(do_GetService("@mozilla.org/gfx/screenmanager;1")); if (sm) { nsIntRect frameRect = GetScreenRect(); // ScreenForRect requires display pixels, so scale from device pix double scale; window->GetUnscaledDevicePixelsPerCSSPixel(&scale); sm->ScreenForRect(NSToIntRound(frameRect.x / scale), NSToIntRound(frameRect.y / scale), 1, 1, getter_AddRefs(screen)); if (screen) { LayoutDeviceIntRect screenRect; screen->GetRect(&screenRect.x, &screenRect.y, &screenRect.width, &screenRect.height); rect.IntersectRect(rect, screenRect); } } } else if (menuPopupFrame) { nsRect frameRect = menuPopupFrame->GetScreenRectInAppUnits(); nsIFrame* rootFrame = aPresContext->PresShell()->FrameManager()->GetRootFrame(); nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits(); nsPopupLevel popupLevel = menuPopupFrame->PopupLevel(); int32_t appPerDev = aPresContext->AppUnitsPerDevPixel(); LayoutDeviceIntRect screenRect = menuPopupFrame->GetConstraintRect (LayoutDeviceIntRect::FromAppUnitsToNearest(frameRect, appPerDev), // round using ...ToInside as it's better to be a pixel too small // than be too large. If the popup is too large it could get flipped // to the opposite side of the anchor point while resizing. LayoutDeviceIntRect::FromAppUnitsToInside(rootScreenRect, appPerDev), popupLevel); rect.IntersectRect(rect, screenRect); } if (contentToResize) { // convert the rectangle into css pixels. When changing the size in a // direction, don't allow the new size to be less that the resizer's // size. This ensures that content isn't resized too small as to make // the resizer invisible. nsRect appUnitsRect = ToAppUnits(rect.ToUnknownRect(), aPresContext->AppUnitsPerDevPixel()); if (appUnitsRect.width < mRect.width && mouseMove.x) appUnitsRect.width = mRect.width; if (appUnitsRect.height < mRect.height && mouseMove.y) appUnitsRect.height = mRect.height; nsIntRect cssRect = appUnitsRect.ToInsidePixels(nsPresContext::AppUnitsPerCSSPixel()); LayoutDeviceIntRect oldRect; nsWeakFrame weakFrame(menuPopupFrame); if (menuPopupFrame) { nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget(); if (widget) widget->GetScreenBounds(oldRect); // convert the new rectangle into outer window coordinates LayoutDeviceIntPoint clientOffset = widget->GetClientOffset(); rect.x -= clientOffset.x; rect.y -= clientOffset.y; } SizeInfo sizeInfo, originalSizeInfo; sizeInfo.width.AppendInt(cssRect.width); sizeInfo.height.AppendInt(cssRect.height); ResizeContent(contentToResize, direction, sizeInfo, &originalSizeInfo); MaybePersistOriginalSize(contentToResize, originalSizeInfo); // Move the popup to the new location unless it is anchored, since // the position shouldn't change. nsMenuPopupFrame::SetPopupPosition // will instead ensure that the popup's position is anchored at the // right place. if (weakFrame.IsAlive() && (oldRect.x != rect.x || oldRect.y != rect.y) && (!menuPopupFrame->IsAnchored() || menuPopupFrame->PopupLevel() != ePopupLevelParent)) { CSSPoint cssPos = rect.TopLeft() / aPresContext->CSSToDevPixelScale(); menuPopupFrame->MoveTo(RoundedToInt(cssPos), true); } } else { window->SetPositionAndSize(rect.x, rect.y, rect.width, rect.height, true); // do the repaint. } doDefault = false; } } break; case eMouseClick: { WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); if (mouseEvent->IsLeftClickEvent()) { MouseClicked(mouseEvent); } break; } case eMouseDoubleClick: if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { nsCOMPtr<nsIBaseWindow> window; nsIPresShell* presShell = aPresContext->GetPresShell(); nsIContent* contentToResize = GetContentToResize(presShell, getter_AddRefs(window)); if (contentToResize) { nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(contentToResize->GetPrimaryFrame()); if (menuPopupFrame) break; // Don't restore original sizing for menupopup frames until // we handle screen constraints here. (Bug 357725) RestoreOriginalSize(contentToResize); } } break; default: break; } if (!doDefault) *aEventStatus = nsEventStatus_eConsumeNoDefault; if (doDefault && weakFrame.IsAlive()) return nsTitleBarFrame::HandleEvent(aPresContext, aEvent, aEventStatus); return NS_OK; }
void nsView::DoResetWidgetBounds(bool aMoveOnly, bool aInvalidateChangedSize) { // The geometry of a root view's widget is controlled externally, // NOT by sizing or positioning the view if (mViewManager->GetRootView() == this) { return; } NS_PRECONDITION(mWindow, "Why was this called??"); // Hold this ref to make sure it stays alive. nsCOMPtr<nsIWidget> widget = mWindow; // Stash a copy of these and use them so we can handle this being deleted (say // from sync painting/flushing from Show/Move/Resize on the widget). LayoutDeviceIntRect newBounds; RefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext(); nsWindowType type = widget->WindowType(); LayoutDeviceIntRect curBounds; widget->GetClientBounds(curBounds); bool invisiblePopup = type == eWindowType_popup && ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) || mVis == nsViewVisibility_kHide); if (invisiblePopup) { // We're going to hit the early exit below, avoid calling CalcWidgetBounds. } else { newBounds = LayoutDeviceIntRect::FromUnknownRect(CalcWidgetBounds(type)); } bool curVisibility = widget->IsVisible(); bool newVisibility = IsEffectivelyVisible(); if (curVisibility && !newVisibility) { widget->Show(false); } if (invisiblePopup) { // Don't manipulate empty or hidden popup widgets. For example there's no // point moving hidden comboboxes around, or doing X server roundtrips // to compute their true screen position. This could mean that WidgetToScreen // operations on these widgets don't return up-to-date values, but popup // positions aren't reliable anyway because of correction to be on or off-screen. return; } bool changedPos = curBounds.TopLeft() != newBounds.TopLeft(); bool changedSize = curBounds.Size() != newBounds.Size(); // Child views are never attached to top level widgets, this is safe. // Coordinates are converted to display pixels for window Move/Resize APIs, // because of the potential for device-pixel coordinate spaces for mixed // hidpi/lodpi screens to overlap each other and result in bad placement // (bug 814434). double invScale; // Bug 861270: for correct widget manipulation at arbitrary scale factors, // prefer to base scaling on widget->GetDefaultScale(). But only do this if // it matches the view manager's device context scale after allowing for the // quantization to app units, because of OS X multiscreen issues (where the // only two scales are 1.0 or 2.0, and so the quantization doesn't actually // cause problems anyhow). // In the case of a mismatch, fall back to scaling based on the dev context's // AppUnitsPerDevPixelAtUnitFullZoom value. On platforms where the device-pixel // scale is uniform across all displays (currently all except OS X), we'll // always use the precise value from mWindow->GetDefaultScale here. CSSToLayoutDeviceScale scale = widget->GetDefaultScale(); if (NSToIntRound(60.0 / scale.scale) == dx->AppUnitsPerDevPixelAtUnitFullZoom()) { invScale = 1.0 / scale.scale; } else { invScale = dx->AppUnitsPerDevPixelAtUnitFullZoom() / 60.0; } if (changedPos) { if (changedSize && !aMoveOnly) { widget->ResizeClient(newBounds.x * invScale, newBounds.y * invScale, newBounds.width * invScale, newBounds.height * invScale, aInvalidateChangedSize); } else { widget->MoveClient(newBounds.x * invScale, newBounds.y * invScale); } } else { if (changedSize && !aMoveOnly) { widget->ResizeClient(newBounds.width * invScale, newBounds.height * invScale, aInvalidateChangedSize); } // else do nothing! } if (!curVisibility && newVisibility) { widget->Show(true); } }
/** * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in * every widget child of aWidgetView, plus aWidgetView's own widget */ void nsViewManager::InvalidateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion) { NS_ASSERTION(aWidgetView->GetViewManager() == this, "InvalidateWidgetArea called on view we don't own"); nsIWidget* widget = aWidgetView->GetWidget(); #if 0 nsRect dbgBounds = aDamagedRegion.GetBounds(); printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n", aWidgetView, aWidgetView->IsAttachedToTopLevel(), widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height); #endif // If the widget is hidden, it don't cover nothing if (widget && !widget->IsVisible()) { return; } if (!widget) { // The root view or a scrolling view might not have a widget // (for example, during printing). We get here when we scroll // during printing to show selected options in a listbox, for example. return; } // Update all child widgets with the damage. In the process, // accumulate the union of all the child widget areas, or at least // some subset of that. nsRegion children; if (widget->GetTransparencyMode() != eTransparencyTransparent) { for (nsIWidget* childWidget = widget->GetFirstChild(); childWidget; childWidget = childWidget->GetNextSibling()) { nsView* view = nsView::GetViewFor(childWidget); NS_ASSERTION(view != aWidgetView, "will recur infinitely"); nsWindowType type = childWidget->WindowType(); if (view && childWidget->IsVisible() && type != eWindowType_popup) { NS_ASSERTION(childWidget->IsPlugin(), "Only plugin or popup widgets can be children!"); // We do not need to invalidate in plugin widgets, but we should // exclude them from the invalidation region IF we're not on // Mac. On Mac we need to draw under plugin widgets, because // plugin widgets are basically invisible #ifndef XP_MACOSX // GetBounds should compensate for chrome on a toplevel widget LayoutDeviceIntRect bounds; childWidget->GetBounds(bounds); nsTArray<LayoutDeviceIntRect> clipRects; childWidget->GetWindowClipRegion(&clipRects); for (uint32_t i = 0; i < clipRects.Length(); ++i) { nsRect rr = LayoutDeviceIntRect::ToAppUnits( clipRects[i] + bounds.TopLeft(), AppUnitsPerDevPixel()); children.Or(children, rr - aWidgetView->ViewToWidgetOffset()); children.SimplifyInward(20); } #endif } } } nsRegion leftOver; leftOver.Sub(aDamagedRegion, children); if (!leftOver.IsEmpty()) { for (auto iter = leftOver.RectIter(); !iter.Done(); iter.Next()) { LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get()); widget->Invalidate(bounds); } } }
nsresult nsTitleBarFrame::HandleEvent(nsPresContext* aPresContext, WidgetGUIEvent* aEvent, nsEventStatus* aEventStatus) { NS_ENSURE_ARG_POINTER(aEventStatus); if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { return NS_OK; } bool doDefault = true; switch (aEvent->mMessage) { case eMouseDown: { if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { // titlebar has no effect in non-chrome shells nsCOMPtr<nsIDocShellTreeItem> dsti = aPresContext->GetDocShell(); if (dsti) { if (dsti->ItemType() == nsIDocShellTreeItem::typeChrome) { // we're tracking. mTrackingMouseMove = true; // start capture. nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED); // remember current mouse coordinates. mLastPoint = aEvent->mRefPoint; } } *aEventStatus = nsEventStatus_eConsumeNoDefault; doDefault = false; } } break; case eMouseUp: { if (mTrackingMouseMove && aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { // we're done tracking. mTrackingMouseMove = false; // end capture nsIPresShell::SetCapturingContent(nullptr, 0); *aEventStatus = nsEventStatus_eConsumeNoDefault; doDefault = false; } } break; case eMouseMove: { if(mTrackingMouseMove) { LayoutDeviceIntPoint nsMoveBy = aEvent->mRefPoint - mLastPoint; nsIFrame* parent = GetParent(); while (parent) { nsMenuPopupFrame* popupFrame = do_QueryFrame(parent); if (popupFrame) break; parent = parent->GetParent(); } // if the titlebar is in a popup, move the popup frame, otherwise // move the widget associated with the window if (parent) { nsMenuPopupFrame* menuPopupFrame = static_cast<nsMenuPopupFrame*>(parent); nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget(); LayoutDeviceIntRect bounds = widget->GetScreenBounds(); CSSPoint cssPos = (bounds.TopLeft() + nsMoveBy) / aPresContext->CSSToDevPixelScale(); menuPopupFrame->MoveTo(RoundedToInt(cssPos), false); } else { nsIPresShell* presShell = aPresContext->PresShell(); nsPIDOMWindowOuter *window = presShell->GetDocument()->GetWindow(); if (window) { window->MoveBy(nsMoveBy.x, nsMoveBy.y); } } *aEventStatus = nsEventStatus_eConsumeNoDefault; doDefault = false; } } break; case eMouseClick: { WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); if (mouseEvent->IsLeftClickEvent()) { MouseClicked(mouseEvent); } break; } default: break; } if ( doDefault ) return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus); else return NS_OK; }
void nsView::DoResetWidgetBounds(bool aMoveOnly, bool aInvalidateChangedSize) { // The geometry of a root view's widget is controlled externally, // NOT by sizing or positioning the view if (mViewManager->GetRootView() == this) { return; } NS_PRECONDITION(mWindow, "Why was this called??"); // Hold this ref to make sure it stays alive. nsCOMPtr<nsIWidget> widget = mWindow; // Stash a copy of these and use them so we can handle this being deleted (say // from sync painting/flushing from Show/Move/Resize on the widget). LayoutDeviceIntRect newBounds; RefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext(); nsWindowType type = widget->WindowType(); LayoutDeviceIntRect curBounds; widget->GetClientBounds(curBounds); bool invisiblePopup = type == eWindowType_popup && ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) || mVis == nsViewVisibility_kHide); if (invisiblePopup) { // We're going to hit the early exit below, avoid calling CalcWidgetBounds. } else { newBounds = CalcWidgetBounds(type); } bool curVisibility = widget->IsVisible(); bool newVisibility = IsEffectivelyVisible(); if (curVisibility && !newVisibility) { widget->Show(false); } if (invisiblePopup) { // Don't manipulate empty or hidden popup widgets. For example there's no // point moving hidden comboboxes around, or doing X server roundtrips // to compute their true screen position. This could mean that WidgetToScreen // operations on these widgets don't return up-to-date values, but popup // positions aren't reliable anyway because of correction to be on or off-screen. return; } bool changedPos = curBounds.TopLeft() != newBounds.TopLeft(); bool changedSize = curBounds.Size() != newBounds.Size(); // Child views are never attached to top level widgets, this is safe. // Coordinates are converted to desktop pixels for window Move/Resize APIs, // because of the potential for device-pixel coordinate spaces for mixed // hidpi/lodpi screens to overlap each other and result in bad placement // (bug 814434). DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScale(); #ifdef XP_MACOSX // On OS X, this can be called before Cocoa has updated the backing scale // factor of our widget, in which case |scale| is wrong here. To work // around this, we check the device context and override |scale| if it // doesn't match. (This happens when a popup window that has previously // been created and hidden is being moved between hi- and lo-dpi screens, // but is not currently visible; Cocoa doesn't notify it of the scale // factor change until it gets shown on the new screen, which is too late // for us because we'll have already done the computations involving scale // here to move/size it.) // It might be better to avoid this by keeping calculations such as // CalcWidgetBounds entirely in appUnits, rather than using device pixels, // but that seems like a more extensive and potentially risky change. int32_t appPerDev = dx->AppUnitsPerDevPixelAtUnitFullZoom(); if (NSToIntRound(60.0 / scale.scale) != appPerDev) { scale = DesktopToLayoutDeviceScale(60.0 / appPerDev); } #endif DesktopRect deskRect = newBounds / scale; if (changedPos) { if (changedSize && !aMoveOnly) { widget->ResizeClient(deskRect.x, deskRect.y, deskRect.width, deskRect.height, aInvalidateChangedSize); } else { widget->MoveClient(deskRect.x, deskRect.y); } } else { if (changedSize && !aMoveOnly) { widget->ResizeClient(deskRect.width, deskRect.height, aInvalidateChangedSize); } // else do nothing! } if (!curVisibility && newVisibility) { widget->Show(true); } }