cef/libcef/browser/views/view_util.cc
npavlov 485f0b9caf Add screen pixel to screen DIP conversion methods.
On Windows these new CefDisplay methods convert between screen pixel coordinates
and screen DIP coordinates. On macOS and Linux these methods just return a copy
of the input coordinates.
2022-10-14 13:55:07 -04:00

320 lines
9.3 KiB
C++

// Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
#include "libcef/browser/views/view_util.h"
#include <utility>
#include "libcef/browser/views/view_adapter.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/non_client_view.h"
#if BUILDFLAG(IS_WIN)
#include "ui/display/win/screen_win.h"
#endif
#if defined(USE_AURA)
#include "ui/aura/window.h"
#include "ui/views/view_constants_aura.h"
#endif
namespace view_util {
namespace {
// Manages the association between views::View and CefView instances.
class UserData : public base::SupportsUserData::Data {
public:
// Create the initial association between the views::View and the CefView. The
// CefView owns the views::View at this stage.
static void Register(CefRefPtr<CefView> cef_view) {
DCHECK(cef_view->IsValid());
DCHECK(!cef_view->IsAttached());
views::View* view = CefViewAdapter::GetFor(cef_view)->Get();
DCHECK(view);
// The CefView should not already be registered.
DCHECK(!view->GetUserData(UserDataKey()));
view->SetUserData(UserDataKey(), base::WrapUnique(new UserData(cef_view)));
}
static CefRefPtr<CefView> GetFor(const views::View* view) {
DCHECK(view);
UserData* data = static_cast<UserData*>(view->GetUserData(UserDataKey()));
if (data)
return data->view_ref_;
return nullptr;
}
// Transfer ownership of the views::View to the caller. The views::View will
// gain a ref-counted reference to the CefView and the CefView will keep an
// unowned reference to the views::View. Destruction of the views::View will
// release the ref-counted reference to the CefView.
[[nodiscard]] static std::unique_ptr<views::View> PassOwnership(
CefRefPtr<CefView> cef_view) {
DCHECK(cef_view->IsValid());
DCHECK(!cef_view->IsAttached());
std::unique_ptr<views::View> view =
CefViewAdapter::GetFor(cef_view)->PassOwnership();
DCHECK(view);
UserData* data = static_cast<UserData*>(view->GetUserData(UserDataKey()));
DCHECK(data);
data->TakeReference();
return view;
}
// The CefView resumes ownership of the views::View. The views::View no longer
// keeps a ref-counted reference to the CefView.
static void ResumeOwnership(CefRefPtr<CefView> cef_view) {
DCHECK(cef_view->IsValid());
DCHECK(cef_view->IsAttached());
CefViewAdapter* adapter = CefViewAdapter::GetFor(cef_view);
adapter->ResumeOwnership();
views::View* view = adapter->Get();
DCHECK(view);
UserData* data = static_cast<UserData*>(view->GetUserData(UserDataKey()));
DCHECK(data);
data->ReleaseReference();
}
private:
friend std::default_delete<UserData>;
explicit UserData(CefRefPtr<CefView> cef_view) : view_ref_(cef_view.get()) {
DCHECK(view_ref_);
}
~UserData() override {
if (view_) {
// The CefView does not own the views::View. Remove the CefView's
// reference to the views::View.
CefViewAdapter::GetFor(view_)->Detach();
}
}
void TakeReference() { view_ = view_ref_; }
void ReleaseReference() { view_ = nullptr; }
static void* UserDataKey() {
// We just need a unique constant. Use the address of a static that
// COMDAT folding won't touch in an optimizing linker.
static int data_key = 0;
return reinterpret_cast<void*>(&data_key);
}
CefRefPtr<CefView> view_;
CefView* view_ref_;
};
} // namespace
const SkColor kDefaultBackgroundColor = SkColorSetARGB(255, 255, 255, 255);
const char kDefaultFontList[] = "Arial, Helvetica, 14px";
void Register(CefRefPtr<CefView> view) {
UserData::Register(view);
}
CefRefPtr<CefView> GetFor(const views::View* view, bool find_known_parent) {
if (!view)
return nullptr;
if (!find_known_parent)
return UserData::GetFor(view);
CefRefPtr<CefView> cef_view;
const views::View* current_view = view;
do {
cef_view = UserData::GetFor(current_view);
if (cef_view)
break;
current_view = current_view->parent();
} while (current_view);
return cef_view;
}
views::View* GetFor(CefRefPtr<CefView> view) {
return CefViewAdapter::GetFor(view)->Get();
}
std::unique_ptr<views::View> PassOwnership(CefRefPtr<CefView> view) {
return UserData::PassOwnership(view);
}
void ResumeOwnership(CefRefPtr<CefView> view) {
UserData::ResumeOwnership(view);
}
CefRefPtr<CefWindow> GetWindowFor(views::Widget* widget) {
CefRefPtr<CefWindow> window;
#if defined(USE_AURA)
// Retrieve the parent Widget for an overlay.
if (widget) {
// See matching logic in CefOverlayViewHost::Init.
auto widget_view =
widget->GetNativeView()->GetProperty(views::kHostViewKey);
if (widget_view) {
widget = widget_view->GetWidget();
}
}
#endif // defined(USE_AURA)
if (widget) {
// The views::WidgetDelegate should be a CefWindowView and |content_view|
// should be the same CefWindowView. However, just in case the views::Widget
// was created by something else let's go about this the safer way.
views::View* content_view = widget->widget_delegate()->GetContentsView();
CefRefPtr<CefView> cef_view = GetFor(content_view, false);
if (cef_view && cef_view->AsPanel())
window = cef_view->AsPanel()->AsWindow();
// The Window should always exist if we created the views::Widget.
DCHECK(window);
}
return window;
}
display::Display GetDisplayNearestPoint(const gfx::Point& point,
bool input_pixel_coords) {
gfx::Point find_point = point;
#if BUILDFLAG(IS_WIN)
if (input_pixel_coords) {
find_point = gfx::ToFlooredPoint(
display::win::ScreenWin::ScreenToDIPPoint(gfx::PointF(point)));
}
#endif
return display::Screen::GetScreen()->GetDisplayNearestPoint(find_point);
}
display::Display GetDisplayMatchingBounds(const gfx::Rect& bounds,
bool input_pixel_coords) {
gfx::Rect find_bounds = bounds;
#if BUILDFLAG(IS_WIN)
if (input_pixel_coords) {
find_bounds =
display::win::ScreenWin::ScreenToDIPRect(nullptr, find_bounds);
}
#endif
return display::Screen::GetScreen()->GetDisplayMatching(find_bounds);
}
void ConvertPointFromPixels(gfx::Point* point, float device_scale_factor) {
*point = gfx::ToFlooredPoint(
gfx::ScalePoint(gfx::PointF(*point), 1.0f / device_scale_factor));
}
void ConvertPointToPixels(gfx::Point* point, float device_scale_factor) {
*point = gfx::ToFlooredPoint(
gfx::ScalePoint(gfx::PointF(*point), device_scale_factor));
}
#if BUILDFLAG(IS_WIN)
gfx::Point ConvertPointFromPixels(const gfx::Point& point) {
return gfx::ToFlooredPoint(
display::win::ScreenWin::ScreenToDIPPoint(gfx::PointF(point)));
}
gfx::Point ConvertPointToPixels(const gfx::Point& point) {
return display::win::ScreenWin::DIPToScreenPoint(point);
}
#endif // BUILDFLAG(IS_WIN)
bool ConvertPointToScreen(views::View* view,
gfx::Point* point,
bool output_pixel_coords) {
if (!view->GetWidget())
return false;
views::View::ConvertPointToScreen(view, point);
if (output_pixel_coords) {
const display::Display& display = GetDisplayNearestPoint(*point, false);
ConvertPointToPixels(point, display.device_scale_factor());
}
return true;
}
bool ConvertPointFromScreen(views::View* view,
gfx::Point* point,
bool input_pixel_coords) {
if (!view->GetWidget())
return false;
if (input_pixel_coords) {
const display::Display& display = GetDisplayNearestPoint(*point, true);
ConvertPointFromPixels(point, display.device_scale_factor());
}
views::View::ConvertPointFromScreen(view, point);
return true;
}
bool ConvertPointToWindow(views::View* view, gfx::Point* point) {
views::Widget* widget = view->GetWidget();
if (!widget)
return false;
views::View::ConvertPointToWidget(view, point);
if (widget->non_client_view()) {
views::NonClientFrameView* non_client_frame_view =
widget->non_client_view()->frame_view();
if (non_client_frame_view) {
// When using a custom drawn NonClientFrameView the native Window will not
// know the actual client bounds. Adjust the native Window bounds for the
// reported client bounds.
const gfx::Rect& client_bounds =
non_client_frame_view->GetBoundsForClientView();
*point -= client_bounds.OffsetFromOrigin();
}
}
return true;
}
bool ConvertPointFromWindow(views::View* view, gfx::Point* point) {
views::Widget* widget = view->GetWidget();
if (!widget)
return false;
if (widget->non_client_view()) {
views::NonClientFrameView* non_client_frame_view =
widget->non_client_view()->frame_view();
if (non_client_frame_view) {
// When using a custom drawn NonClientFrameView the native Window will not
// know the actual client bounds. Adjust the native Window bounds for the
// reported client bounds.
const gfx::Rect& client_bounds =
non_client_frame_view->GetBoundsForClientView();
*point += client_bounds.OffsetFromOrigin();
}
}
views::View::ConvertPointFromWidget(view, point);
return true;
}
} // namespace view_util