diff options
Diffstat (limited to 'chrome/views/hwnd_view.cc')
-rw-r--r-- | chrome/views/hwnd_view.cc | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/chrome/views/hwnd_view.cc b/chrome/views/hwnd_view.cc new file mode 100644 index 0000000..82b2893 --- /dev/null +++ b/chrome/views/hwnd_view.cc @@ -0,0 +1,227 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/views/hwnd_view.h" + +#include "chrome/common/gfx/chrome_canvas.h" +#include "chrome/common/win_util.h" +#include "chrome/views/focus_manager.h" +#include "chrome/views/scroll_view.h" +#include "chrome/views/view_container.h" +#include "base/logging.h" + +namespace ChromeViews { + +static const char kViewClassName[] = "chrome/views/HWNDView"; + +HWNDView::HWNDView() : + hwnd_(0), + preferred_size_(0, 0), + installed_clip_(false), + fast_resize_(false), + focus_view_(NULL) { + // HWNDs are placed relative to the root. As such, we need to + // know when the position of any ancestor changes, or our visibility relative + // to other views changed as it'll effect our position relative to the root. + SetNotifyWhenVisibleBoundsInRootChanges(true); +} + +HWNDView::~HWNDView() { +} + +void HWNDView::Attach(HWND hwnd) { + DCHECK(hwnd_ == NULL); + hwnd_ = hwnd; + + // First hide the new window. We don't want anything to draw (like sub-hwnd + // borders), when we change the parent below. + ShowWindow(hwnd_, SW_HIDE); + + // Need to set the HWND's parent before changing its size to avoid flashing. + ::SetParent(hwnd_, GetViewContainer()->GetHWND()); + UpdateHWNDBounds(); + + // Register with the focus manager so the associated view is focused when the + // native control gets the focus. + FocusManager::InstallFocusSubclass(hwnd_, focus_view_ ? focus_view_ : this); +} + +void HWNDView::Detach() { + DCHECK(hwnd_); + FocusManager::UninstallFocusSubclass(hwnd_); + hwnd_ = NULL; + installed_clip_ = false; +} + +void HWNDView::SetAssociatedFocusView(View* view) { + DCHECK(!::IsWindow(hwnd_)); + focus_view_ = view; +} + +HWND HWNDView::GetHWND() const { + return hwnd_; +} + +void HWNDView::UpdateHWNDBounds() { + if (!hwnd_) + return; + + // Since HWNDs know nothing about the View hierarchy (they are direct children + // of the ViewContainer that hosts our View hierarchy) they need to be + // positioned in the coordinate system of the ViewContainer, not the current + // view. + CPoint top_left; + + top_left.x = top_left.y = 0; + ConvertPointToViewContainer(this, &top_left); + + gfx::Rect vis_bounds = GetVisibleBounds(); + bool visible = !vis_bounds.IsEmpty(); + + if (visible && !fast_resize_) { + if (vis_bounds.width() != bounds_.Width() || + vis_bounds.height() != bounds_.Height()) { + // Only a portion of the HWND is really visible. + int x = vis_bounds.x(); + int y = vis_bounds.y(); + HRGN clip_region = CreateRectRgn(x, y, x + vis_bounds.width(), + y + vis_bounds.height()); + // NOTE: SetWindowRgn owns the region (as well as the deleting the + // current region), as such we don't delete the old region. + SetWindowRgn(hwnd_, clip_region, FALSE); + installed_clip_ = true; + } else if (installed_clip_) { + // The whole HWND is visible but we installed a clip on the HWND, + // uninstall it. + SetWindowRgn(hwnd_, 0, FALSE); + installed_clip_ = false; + } + } + + if (visible) { + UINT swp_flags; + swp_flags = SWP_DEFERERASE | + SWP_NOACTIVATE | + SWP_NOCOPYBITS | + SWP_NOOWNERZORDER | + SWP_NOZORDER; + // Only send the SHOWWINDOW flag if we're invisible, to avoid flashing. + if (!::IsWindowVisible(hwnd_)) + swp_flags = (swp_flags | SWP_SHOWWINDOW) & ~SWP_NOREDRAW; + + if (fast_resize_) { + // In a fast resize, we move the window and clip it with SetWindowRgn. + CRect rect; + GetWindowRect(hwnd_, &rect); + ::SetWindowPos(hwnd_, 0, top_left.x, top_left.y, rect.Width(), + rect.Height(), swp_flags); + + HRGN clip_region = CreateRectRgn(0, 0, + bounds_.Width(), + bounds_.Height()); + SetWindowRgn(hwnd_, clip_region, FALSE); + installed_clip_ = true; + } else { + ::SetWindowPos(hwnd_, 0, top_left.x, top_left.y, bounds_.Width(), + bounds_.Height(), swp_flags); + } + } else if (::IsWindowVisible(hwnd_)) { + // The window is currently visible, but its clipped by another view. Hide + // it. + ::SetWindowPos(hwnd_, 0, 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | + SWP_NOREDRAW | SWP_NOOWNERZORDER ); + } +} + +void HWNDView::DidChangeBounds(const CRect& previous, const CRect& current) { + UpdateHWNDBounds(); +} + +void HWNDView::VisibilityChanged(View* starting_from, bool is_visible) { + //UpdateHWNDBounds(); + if (IsVisibleInRootView()) + ::ShowWindow(hwnd_, SW_SHOW); + else + ::ShowWindow(hwnd_, SW_HIDE); +} + +void HWNDView::GetPreferredSize(CSize *out) { + out->cx = preferred_size_.cx; + out->cy = preferred_size_.cy; +} + +void HWNDView::SetPreferredSize(const CSize& size) { + preferred_size_ = size; +} + +void HWNDView::ViewHierarchyChanged(bool is_add, View *parent, View *child) { + if (hwnd_) { + ViewContainer* vc = GetViewContainer(); + if (is_add && vc) { + HWND parent = ::GetParent(hwnd_); + HWND vc_hwnd = vc->GetHWND(); + if (parent != vc_hwnd) { + ::SetParent(hwnd_, vc_hwnd); + } + if (IsVisibleInRootView()) + ::ShowWindow(hwnd_, SW_SHOW); + else + ::ShowWindow(hwnd_, SW_HIDE); + UpdateHWNDBounds(); + } else if (!is_add) { + ::ShowWindow(hwnd_, SW_HIDE); + ::SetParent(hwnd_, NULL); + } + } +} + +void HWNDView::VisibleBoundsInRootChanged() { + UpdateHWNDBounds(); +} + +void HWNDView::Paint(ChromeCanvas* canvas) { + // On Vista, the area behind our window is black (due to the Aero Glass) + // this means that during a fast resize (where our content doesn't draw + // over the full size of our HWND, and the HWND background color + // doesn't show up), we need to cover that blackness with something so + // that fast resizes don't result in black flash. + // + // It would be nice if this used some approximation of the page's + // current background color. + if (installed_clip_ && win_util::ShouldUseVistaFrame()) + canvas->FillRectInt(SkColorSetRGB(255, 255, 255), 0, 0, + bounds_.Width(), bounds_.Height()); +} + +std::string HWNDView::GetClassName() const { + return kViewClassName; +} + +} |