minimize_button_metrics_win.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/views/frame/minimize_button_metrics_win.h"
6
7#include "base/logging.h"
8#include "base/i18n/rtl.h"
9#include "ui/base/win/shell.h"
10#include "ui/gfx/win/dpi.h"
11
12namespace {
13
14int GetMinimizeButtonOffsetForWindow(HWND hwnd) {
15  // The WM_GETTITLEBARINFOEX message can fail if we are not active/visible. By
16  // fail we get a location of 0; the return status code is always the same and
17  // similarly the state never seems to change (titlebar_info.rgstate).
18  TITLEBARINFOEX titlebar_info = {0};
19  titlebar_info.cbSize = sizeof(TITLEBARINFOEX);
20  SendMessage(hwnd, WM_GETTITLEBARINFOEX, 0,
21              reinterpret_cast<WPARAM>(&titlebar_info));
22
23  if (titlebar_info.rgrect[2].left == titlebar_info.rgrect[2].right ||
24      (titlebar_info.rgstate[2] & (STATE_SYSTEM_INVISIBLE ||
25                                   STATE_SYSTEM_OFFSCREEN ||
26                                   STATE_SYSTEM_UNAVAILABLE))) {
27    return 0;
28  }
29
30  // Most versions of Windows return screen coordinates for
31  // WM_GETTITLEBARINFOEX. Since chrome is not dpi aware (currently) we need to
32  // unscale these coordinates. Surface Pro seems to be unique, in that it
33  // returns local coordinates (eg they don't need to be scaled). There doesn't
34  // appear to be a clear way to detect this, so we assume that if the minimize
35  // button is outside the bounds of the window coordinates are scaled.
36  RECT window_rect = {0};
37  GetWindowRect(hwnd, &window_rect);
38  POINT minimize_button_corner = { titlebar_info.rgrect[2].left, 0 };
39  if (minimize_button_corner.x > window_rect.right) {
40    minimize_button_corner.x =
41        static_cast<int>(minimize_button_corner.x /
42                         gfx::win::GetUndocumentedDPIScale());
43  }
44  MapWindowPoints(HWND_DESKTOP, hwnd, &minimize_button_corner, 1);
45  return minimize_button_corner.x / gfx::win::GetDeviceScaleFactor();
46}
47
48}  // namespace
49
50// static
51int MinimizeButtonMetrics::last_cached_minimize_button_x_delta_ = 0;
52
53MinimizeButtonMetrics::MinimizeButtonMetrics()
54    : hwnd_(NULL),
55      cached_minimize_button_x_delta_(last_cached_minimize_button_x_delta_),
56      was_activated_(false) {
57}
58
59MinimizeButtonMetrics::~MinimizeButtonMetrics() {
60}
61
62void MinimizeButtonMetrics::Init(HWND hwnd) {
63  DCHECK(!hwnd_);
64  hwnd_ = hwnd;
65}
66
67void MinimizeButtonMetrics::OnHWNDActivated() {
68  was_activated_ = true;
69  // NOTE: we don't cache here as it seems only after the activate is the value
70  // correct.
71}
72
73int MinimizeButtonMetrics::GetMinimizeButtonOffsetX() const {
74  // Under DWM WM_GETTITLEBARINFOEX won't return the right thing until after
75  // WM_NCACTIVATE (maybe it returns classic values?). In an attempt to return a
76  // consistant value we cache the last value across instances and use it until
77  // we get the activate.
78  if (was_activated_ || !ui::win::IsAeroGlassEnabled() ||
79      cached_minimize_button_x_delta_ == 0) {
80    const int minimize_button_offset = GetAndCacheMinimizeButtonOffsetX();
81    if (minimize_button_offset > 0)
82      return minimize_button_offset;
83  }
84
85  // If we fail to get the minimize button offset via the WM_GETTITLEBARINFOEX
86  // message then calculate and return this via the
87  // cached_minimize_button_x_delta_ member value. Please see
88  // CacheMinimizeButtonDelta() for more details.
89  DCHECK(cached_minimize_button_x_delta_);
90
91  if (base::i18n::IsRTL())
92    return cached_minimize_button_x_delta_;
93
94  RECT client_rect = {0};
95  GetClientRect(hwnd_, &client_rect);
96  return client_rect.right - cached_minimize_button_x_delta_;
97}
98
99int MinimizeButtonMetrics::GetAndCacheMinimizeButtonOffsetX() const {
100  const int minimize_button_offset = GetMinimizeButtonOffsetForWindow(hwnd_);
101  if (minimize_button_offset <= 0)
102    return 0;
103
104  if (base::i18n::IsRTL()) {
105    cached_minimize_button_x_delta_ = minimize_button_offset;
106  } else {
107    RECT client_rect = {0};
108    GetClientRect(hwnd_, &client_rect);
109    cached_minimize_button_x_delta_ =
110        client_rect.right - minimize_button_offset;
111  }
112  last_cached_minimize_button_x_delta_ = cached_minimize_button_x_delta_;
113  return minimize_button_offset;
114}
115