| // Copyright (c) 2012 The Chromium 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 "chrome/browser/ui/views/frame/minimize_button_metrics_win.h" |
| |
| #include "base/logging.h" |
| #include "base/i18n/rtl.h" |
| #include "ui/base/win/shell.h" |
| #include "ui/gfx/win/dpi.h" |
| |
| namespace { |
| |
| int GetMinimizeButtonOffsetForWindow(HWND hwnd) { |
| // The WM_GETTITLEBARINFOEX message can fail if we are not active/visible. By |
| // fail we get a location of 0; the return status code is always the same and |
| // similarly the state never seems to change (titlebar_info.rgstate). |
| TITLEBARINFOEX titlebar_info = {0}; |
| titlebar_info.cbSize = sizeof(TITLEBARINFOEX); |
| SendMessage(hwnd, WM_GETTITLEBARINFOEX, 0, |
| reinterpret_cast<WPARAM>(&titlebar_info)); |
| |
| if (titlebar_info.rgrect[2].left == titlebar_info.rgrect[2].right || |
| (titlebar_info.rgstate[2] & (STATE_SYSTEM_INVISIBLE || |
| STATE_SYSTEM_OFFSCREEN || |
| STATE_SYSTEM_UNAVAILABLE))) { |
| return 0; |
| } |
| |
| // Most versions of Windows return screen coordinates for |
| // WM_GETTITLEBARINFOEX. Since chrome is not dpi aware (currently) we need to |
| // unscale these coordinates. Surface Pro seems to be unique, in that it |
| // returns local coordinates (eg they don't need to be scaled). There doesn't |
| // appear to be a clear way to detect this, so we assume that if the minimize |
| // button is outside the bounds of the window coordinates are scaled. |
| RECT window_rect = {0}; |
| GetWindowRect(hwnd, &window_rect); |
| POINT minimize_button_corner = { titlebar_info.rgrect[2].left, 0 }; |
| if (minimize_button_corner.x > window_rect.right) { |
| minimize_button_corner.x = |
| static_cast<int>(minimize_button_corner.x / |
| gfx::win::GetUndocumentedDPIScale()); |
| } |
| MapWindowPoints(HWND_DESKTOP, hwnd, &minimize_button_corner, 1); |
| return minimize_button_corner.x / gfx::win::GetDeviceScaleFactor(); |
| } |
| |
| } // namespace |
| |
| // static |
| int MinimizeButtonMetrics::last_cached_minimize_button_x_delta_ = 0; |
| |
| MinimizeButtonMetrics::MinimizeButtonMetrics() |
| : hwnd_(NULL), |
| cached_minimize_button_x_delta_(last_cached_minimize_button_x_delta_), |
| was_activated_(false) { |
| } |
| |
| MinimizeButtonMetrics::~MinimizeButtonMetrics() { |
| } |
| |
| void MinimizeButtonMetrics::Init(HWND hwnd) { |
| DCHECK(!hwnd_); |
| hwnd_ = hwnd; |
| } |
| |
| void MinimizeButtonMetrics::OnHWNDActivated() { |
| was_activated_ = true; |
| // NOTE: we don't cache here as it seems only after the activate is the value |
| // correct. |
| } |
| |
| int MinimizeButtonMetrics::GetMinimizeButtonOffsetX() const { |
| // Under DWM WM_GETTITLEBARINFOEX won't return the right thing until after |
| // WM_NCACTIVATE (maybe it returns classic values?). In an attempt to return a |
| // consistant value we cache the last value across instances and use it until |
| // we get the activate. |
| if (was_activated_ || !ui::win::IsAeroGlassEnabled() || |
| cached_minimize_button_x_delta_ == 0) { |
| const int minimize_button_offset = GetAndCacheMinimizeButtonOffsetX(); |
| if (minimize_button_offset > 0) |
| return minimize_button_offset; |
| } |
| |
| // If we fail to get the minimize button offset via the WM_GETTITLEBARINFOEX |
| // message then calculate and return this via the |
| // cached_minimize_button_x_delta_ member value. Please see |
| // CacheMinimizeButtonDelta() for more details. |
| DCHECK(cached_minimize_button_x_delta_); |
| |
| if (base::i18n::IsRTL()) |
| return cached_minimize_button_x_delta_; |
| |
| RECT client_rect = {0}; |
| GetClientRect(hwnd_, &client_rect); |
| return client_rect.right - cached_minimize_button_x_delta_; |
| } |
| |
| int MinimizeButtonMetrics::GetAndCacheMinimizeButtonOffsetX() const { |
| const int minimize_button_offset = GetMinimizeButtonOffsetForWindow(hwnd_); |
| if (minimize_button_offset <= 0) |
| return 0; |
| |
| if (base::i18n::IsRTL()) { |
| cached_minimize_button_x_delta_ = minimize_button_offset; |
| } else { |
| RECT client_rect = {0}; |
| GetClientRect(hwnd_, &client_rect); |
| cached_minimize_button_x_delta_ = |
| client_rect.right - minimize_button_offset; |
| } |
| last_cached_minimize_button_x_delta_ = cached_minimize_button_x_delta_; |
| return minimize_button_offset; |
| } |