diff options
-rw-r--r-- | views/widget/widget_win.cc | 14 | ||||
-rw-r--r-- | views/widget/widget_win.h | 14 | ||||
-rw-r--r-- | views/window/window_win.cc | 122 | ||||
-rw-r--r-- | views/window/window_win.h | 3 |
4 files changed, 125 insertions, 28 deletions
diff --git a/views/widget/widget_win.cc b/views/widget/widget_win.cc index 353fe4b..3d3a2b7 100644 --- a/views/widget/widget_win.cc +++ b/views/widget/widget_win.cc @@ -434,7 +434,7 @@ void WidgetWin::SchedulePaintInRect(const gfx::Rect& rect) { if (use_layered_buffer_) { // We must update the back-buffer immediately, since Windows' handling of // invalid rects is somewhat mysterious. - layered_window_invalid_rect_ = layered_window_invalid_rect_.Union(rect); + invalid_rect_ = invalid_rect_.Union(rect); // In some situations, such as drag and drop, when Windows itself runs a // nested message loop our message loop appears to be starved and we don't @@ -1080,15 +1080,15 @@ void WidgetWin::RedrawInvalidRect() { } void WidgetWin::RedrawLayeredWindowContents() { - if (layered_window_invalid_rect_.IsEmpty()) + if (invalid_rect_.IsEmpty()) return; // We need to clip to the dirty rect ourselves. layered_window_contents_->save(SkCanvas::kClip_SaveFlag); - layered_window_contents_->ClipRectInt(layered_window_invalid_rect_.x(), - layered_window_invalid_rect_.y(), - layered_window_invalid_rect_.width(), - layered_window_invalid_rect_.height()); + layered_window_contents_->ClipRectInt(invalid_rect_.x(), + invalid_rect_.y(), + invalid_rect_.width(), + invalid_rect_.height()); GetWidget()->GetRootView()->Paint(layered_window_contents_.get()); layered_window_contents_->restore(); @@ -1101,7 +1101,7 @@ void WidgetWin::RedrawLayeredWindowContents() { BLENDFUNCTION blend = {AC_SRC_OVER, 0, layered_alpha_, AC_SRC_ALPHA}; UpdateLayeredWindow(hwnd(), NULL, &position, &size, dib_dc, &zero, RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); - layered_window_invalid_rect_.SetRect(0, 0, 0, 0); + invalid_rect_.SetRect(0, 0, 0, 0); layered_window_contents_->endPlatformPaint(); } diff --git a/views/widget/widget_win.h b/views/widget/widget_win.h index c659b61..79f3f49 100644 --- a/views/widget/widget_win.h +++ b/views/widget/widget_win.h @@ -397,6 +397,8 @@ class WidgetWin : public ui::WindowImpl, // Are a subclass of WindowWin? bool is_window_; + const gfx::Rect& invalid_rect() const { return invalid_rect_; } + private: typedef ScopedVector<ui::ViewProp> ViewProps; @@ -460,10 +462,14 @@ class WidgetWin : public ui::WindowImpl, // window. scoped_ptr<gfx::CanvasSkia> layered_window_contents_; - // We must track the invalid rect for a layered window ourselves, since - // Windows will not do this properly with InvalidateRect()/GetUpdateRect(). - // (In fact, it'll return misleading information from GetUpdateRect()). - gfx::Rect layered_window_invalid_rect_; + // We must track the invalid rect ourselves, for two reasons: + // For layered windows, Windows will not do this properly with + // InvalidateRect()/GetUpdateRect(). (In fact, it'll return misleading + // information from GetUpdateRect()). + // We also need to keep track of the invalid rectangle for the RootView should + // we need to paint the non-client area. The data supplied to WM_NCPAINT seems + // to be insufficient. + gfx::Rect invalid_rect_; // A factory that allows us to schedule a redraw for layered windows. ScopedRunnableMethodFactory<WidgetWin> paint_layered_window_factory_; diff --git a/views/window/window_win.cc b/views/window/window_win.cc index 2845379..f50cefa 100644 --- a/views/window/window_win.cc +++ b/views/window/window_win.cc @@ -246,8 +246,7 @@ WindowWin::WindowWin(internal::NativeWindowDelegate* delegate) ignore_pos_changes_factory_(this), force_hidden_count_(0), is_right_mouse_pressed_on_caption_(false), - last_monitor_(NULL), - is_in_size_move_(false) { + last_monitor_(NULL) { is_window_ = true; // Initialize these values to 0 so that subclasses can override the default // behavior before calling Init. @@ -404,23 +403,13 @@ LRESULT WindowWin::OnDwmCompositionChanged(UINT msg, WPARAM w_param, } void WindowWin::OnEnterSizeMove() { - is_in_size_move_ = true; WidgetWin::OnEnterSizeMove(); delegate_->OnNativeWindowBeginUserBoundsChange(); } void WindowWin::OnExitSizeMove() { - is_in_size_move_ = false; WidgetWin::OnExitSizeMove(); delegate_->OnNativeWindowEndUserBoundsChange(); - - if (!GetWindow()->ShouldUseNativeFrame()) { - // Sending SWP_FRAMECHANGED forces a non-client repaint, which fixes the - // glitch in rendering the bottom pixel of the window caused by us - // offsetting the client rect there (See comment in GetClientAreaInsets()). - SetWindowPos(NULL, 0, 0, 0, 0, - SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER); - } } void WindowWin::OnFinalMessage(HWND window) { @@ -687,11 +676,116 @@ LRESULT WindowWin::OnNCHitTest(const CPoint& point) { return WidgetWin::OnNCHitTest(point); } +namespace { +struct ClipState { + // The window being painted. + HWND parent; + + // DC painting to. + HDC dc; + + // Origin of the window in terms of the screen. + int x; + int y; +}; + +// See comments in OnNCPaint for details of this function. +static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) { + ClipState* clip_state = reinterpret_cast<ClipState*>(param); + if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) { + RECT bounds; + GetWindowRect(window, &bounds); + ExcludeClipRect(clip_state->dc, + bounds.left - clip_state->x, + bounds.top - clip_state->y, + bounds.right - clip_state->x, + bounds.bottom - clip_state->y); + } + return TRUE; +} +} // namespace + void WindowWin::OnNCPaint(HRGN rgn) { + // We only do non-client painting if we're not using the native frame. + // It's required to avoid some native painting artifacts from appearing when + // the window is resized. + if (GetWindow()->ShouldUseNativeFrame()) { + WidgetWin::OnNCPaint(rgn); + return; + } + + // We have an NC region and need to paint it. We expand the NC region to + // include the dirty region of the root view. This is done to minimize + // paints. + CRect window_rect; + GetWindowRect(&window_rect); + + if (window_rect.Width() != GetWidget()->GetRootView()->width() || + window_rect.Height() != GetWidget()->GetRootView()->height()) { + // If the size of the window differs from the size of the root view it + // means we're being asked to paint before we've gotten a WM_SIZE. This can + // happen when the user is interactively resizing the window. To avoid + // mass flickering we don't do anything here. Once we get the WM_SIZE we'll + // reset the region of the window which triggers another WM_NCPAINT and + // all is well. + return; + } + + CRect dirty_region; + // A value of 1 indicates paint all. + if (!rgn || rgn == reinterpret_cast<HRGN>(1)) { + dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height()); + } else { + RECT rgn_bounding_box; + GetRgnBox(rgn, &rgn_bounding_box); + if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect)) + return; // Dirty region doesn't intersect window bounds, bale. + + // rgn_bounding_box is in screen coordinates. Map it to window coordinates. + OffsetRect(&dirty_region, -window_rect.left, -window_rect.top); + } + + // In theory GetDCEx should do what we want, but I couldn't get it to work. + // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell + // it doesn't work at all. So, instead we get the DC for the window then + // manually clip out the children. + HDC dc = GetWindowDC(GetNativeView()); + ClipState clip_state; + clip_state.x = window_rect.left; + clip_state.y = window_rect.top; + clip_state.parent = GetNativeView(); + clip_state.dc = dc; + EnumChildWindows(GetNativeView(), &ClipDCToChild, + reinterpret_cast<LPARAM>(&clip_state)); + + RootView* root_view = GetWidget()->GetRootView(); + gfx::Rect old_paint_region = invalid_rect(); + + if (!old_paint_region.IsEmpty()) { + // The root view has a region that needs to be painted. Include it in the + // region we're going to paint. + + CRect old_paint_region_crect = old_paint_region.ToRECT(); + CRect tmp = dirty_region; + UnionRect(&dirty_region, &tmp, &old_paint_region_crect); + } + + GetWidget()->GetRootView()->SchedulePaintInRect(gfx::Rect(dirty_region)); + + // gfx::CanvasSkiaPaint's destructor does the actual painting. As such, wrap + // the following in a block to force paint to occur so that we can release + // the dc. + { + gfx::CanvasSkiaPaint canvas(dc, true, dirty_region.left, + dirty_region.top, dirty_region.Width(), + dirty_region.Height()); + delegate_->AsNativeWidgetDelegate()->OnNativeWidgetPaint(&canvas); + } + + ReleaseDC(GetNativeView(), dc); // When using a custom frame, we want to avoid calling DefWindowProc() since // that may render artifacts. - SetMsgHandled((!IsActive() || is_in_size_move_) && - !GetWindow()->ShouldUseNativeFrame()); + SetMsgHandled(!GetWindow()->ShouldUseNativeFrame()); } LRESULT WindowWin::OnNCUAHDrawCaption(UINT msg, WPARAM w_param, diff --git a/views/window/window_win.h b/views/window/window_win.h index 5270034..c829e13 100644 --- a/views/window/window_win.h +++ b/views/window/window_win.h @@ -268,9 +268,6 @@ class WindowWin : public WidgetWin, DWORD drag_frame_saved_window_style_; DWORD drag_frame_saved_window_ex_style_; - // True when the window is being moved/sized. - bool is_in_size_move_; - DISALLOW_COPY_AND_ASSIGN(WindowWin); }; |