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 "ui/base/layout.h" 6 7 #include <algorithm> 8 #include <cmath> 9 #include <limits> 10 11 #include "base/basictypes.h" 12 #include "base/command_line.h" 13 #include "base/logging.h" 14 #include "build/build_config.h" 15 #include "ui/base/touch/touch_device.h" 16 #include "ui/base/ui_base_switches.h" 17 #include "ui/gfx/display.h" 18 #include "ui/gfx/screen.h" 19 20 #if defined(OS_MACOSX) && !defined(OS_IOS) 21 #include "base/mac/mac_util.h" 22 #endif 23 24 #if defined(OS_WIN) 25 #include "base/win/metro.h" 26 #include "ui/base/win/dpi.h" 27 #include <Windows.h> 28 #endif // defined(OS_WIN) 29 30 #if defined(OS_CHROMEOS) 31 #include "ui/base/resource/resource_bundle.h" 32 #endif 33 34 namespace ui { 35 36 namespace { 37 38 bool ScaleFactorComparator(const ScaleFactor& lhs, const ScaleFactor& rhs){ 39 return GetScaleFactorScale(lhs) < GetScaleFactorScale(rhs); 40 } 41 42 #if defined(OS_WIN) 43 // Helper function that determines whether we want to optimize the UI for touch. 44 bool UseTouchOptimizedUI() { 45 // If --touch-optimized-ui is specified and not set to "auto", then override 46 // the hardware-determined setting (eg. for testing purposes). 47 static bool has_touch_optimized_ui = CommandLine::ForCurrentProcess()-> 48 HasSwitch(switches::kTouchOptimizedUI); 49 if (has_touch_optimized_ui) { 50 const std::string switch_value = CommandLine::ForCurrentProcess()-> 51 GetSwitchValueASCII(switches::kTouchOptimizedUI); 52 53 // Note that simply specifying the switch is the same as enabled. 54 if (switch_value.empty() || 55 switch_value == switches::kTouchOptimizedUIEnabled) { 56 return true; 57 } else if (switch_value == switches::kTouchOptimizedUIDisabled) { 58 return false; 59 } else if (switch_value != switches::kTouchOptimizedUIAuto) { 60 LOG(ERROR) << "Invalid --touch-optimized-ui option: " << switch_value; 61 } 62 } 63 64 // We use the touch layout only when we are running in Metro mode. 65 return base::win::IsMetroProcess() && ui::IsTouchDevicePresent(); 66 } 67 #endif // defined(OS_WIN) 68 69 const float kScaleFactorScales[] = {1.0f, 1.0f, 1.33f, 1.4f, 1.5f, 1.8f, 2.0f}; 70 COMPILE_ASSERT(NUM_SCALE_FACTORS == arraysize(kScaleFactorScales), 71 kScaleFactorScales_incorrect_size); 72 const size_t kScaleFactorScalesLength = arraysize(kScaleFactorScales); 73 74 namespace { 75 76 // Returns the scale factor closest to |scale| from the full list of factors. 77 // Note that it does NOT rely on the list of supported scale factors. 78 // Finding the closest match is inefficient and shouldn't be done frequently. 79 ScaleFactor FindClosestScaleFactorUnsafe(float scale) { 80 float smallest_diff = std::numeric_limits<float>::max(); 81 ScaleFactor closest_match = SCALE_FACTOR_100P; 82 for (int i = SCALE_FACTOR_100P; i < NUM_SCALE_FACTORS; ++i) { 83 const ScaleFactor scale_factor = static_cast<ScaleFactor>(i); 84 float diff = std::abs(kScaleFactorScales[scale_factor] - scale); 85 if (diff < smallest_diff) { 86 closest_match = scale_factor; 87 smallest_diff = diff; 88 } 89 } 90 return closest_match; 91 } 92 93 } // namespace 94 95 std::vector<ScaleFactor>& GetSupportedScaleFactorsInternal() { 96 static std::vector<ScaleFactor>* supported_scale_factors = 97 new std::vector<ScaleFactor>(); 98 if (supported_scale_factors->empty()) { 99 #if !defined(OS_IOS) 100 // On platforms other than iOS, 100P is always a supported scale factor. 101 supported_scale_factors->push_back(SCALE_FACTOR_100P); 102 #endif 103 104 #if defined(OS_ANDROID) 105 const gfx::Display display = 106 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); 107 const float display_density = display.device_scale_factor(); 108 const ScaleFactor closest = FindClosestScaleFactorUnsafe(display_density); 109 if (closest != SCALE_FACTOR_100P) 110 supported_scale_factors->push_back(closest); 111 #elif defined(OS_IOS) 112 gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); 113 if (display.device_scale_factor() > 1.0) { 114 DCHECK_EQ(2.0, display.device_scale_factor()); 115 supported_scale_factors->push_back(SCALE_FACTOR_200P); 116 } else { 117 supported_scale_factors->push_back(SCALE_FACTOR_100P); 118 } 119 #elif defined(OS_MACOSX) 120 if (base::mac::IsOSLionOrLater()) 121 supported_scale_factors->push_back(SCALE_FACTOR_200P); 122 #elif defined(OS_WIN) 123 // Have high-DPI resources for 140% and 180% scaling on Windows based on 124 // default scaling for Metro mode. Round to nearest supported scale in 125 // all cases. 126 if (ui::IsInHighDPIMode()) { 127 supported_scale_factors->push_back(SCALE_FACTOR_140P); 128 supported_scale_factors->push_back(SCALE_FACTOR_180P); 129 } 130 #elif defined(OS_CHROMEOS) 131 // TODO(oshima): Include 200P only if the device support 200P 132 supported_scale_factors->push_back(SCALE_FACTOR_200P); 133 #endif 134 std::sort(supported_scale_factors->begin(), 135 supported_scale_factors->end(), 136 ScaleFactorComparator); 137 } 138 return *supported_scale_factors; 139 } 140 141 } // namespace 142 143 DisplayLayout GetDisplayLayout() { 144 #if defined(OS_WIN) 145 if (UseTouchOptimizedUI()) 146 return LAYOUT_TOUCH; 147 #endif 148 return LAYOUT_DESKTOP; 149 } 150 151 ScaleFactor GetScaleFactorFromScale(float scale) { 152 ScaleFactor closest_match = SCALE_FACTOR_100P; 153 float smallest_diff = std::numeric_limits<float>::max(); 154 const std::vector<ScaleFactor>& supported = 155 GetSupportedScaleFactorsInternal(); 156 for (size_t i = 0; i < supported.size(); ++i) { 157 ScaleFactor scale_factor = supported[i]; 158 float diff = std::abs(kScaleFactorScales[scale_factor] - scale); 159 if (diff < smallest_diff) { 160 closest_match = scale_factor; 161 smallest_diff = diff; 162 } 163 } 164 DCHECK_NE(closest_match, SCALE_FACTOR_NONE); 165 return closest_match; 166 } 167 168 float GetScaleFactorScale(ScaleFactor scale_factor) { 169 return kScaleFactorScales[scale_factor]; 170 } 171 172 ScaleFactor GetMaxScaleFactor() { 173 #if defined(OS_CHROMEOS) 174 return ResourceBundle::GetSharedInstance().max_scale_factor(); 175 #else 176 return GetSupportedScaleFactorsInternal().back(); 177 #endif 178 } 179 180 std::vector<ScaleFactor> GetSupportedScaleFactors() { 181 return GetSupportedScaleFactorsInternal(); 182 } 183 184 bool IsScaleFactorSupported(ScaleFactor scale_factor) { 185 const std::vector<ScaleFactor>& supported = 186 GetSupportedScaleFactorsInternal(); 187 return std::find(supported.begin(), supported.end(), scale_factor) != 188 supported.end(); 189 } 190 191 namespace test { 192 193 void SetSupportedScaleFactors( 194 const std::vector<ui::ScaleFactor>& scale_factors) { 195 std::vector<ui::ScaleFactor>& supported_scale_factors = 196 GetSupportedScaleFactorsInternal(); 197 supported_scale_factors = scale_factors; 198 std::sort(supported_scale_factors.begin(), 199 supported_scale_factors.end(), 200 ScaleFactorComparator); 201 } 202 203 ScopedSetSupportedScaleFactors::ScopedSetSupportedScaleFactors( 204 const std::vector<ui::ScaleFactor>& new_scale_factors) 205 : original_scale_factors_(GetSupportedScaleFactors()) { 206 SetSupportedScaleFactors(new_scale_factors); 207 } 208 209 ScopedSetSupportedScaleFactors::~ScopedSetSupportedScaleFactors() { 210 SetSupportedScaleFactors(original_scale_factors_); 211 } 212 213 } // namespace test 214 215 #if !defined(OS_MACOSX) 216 ScaleFactor GetScaleFactorForNativeView(gfx::NativeView view) { 217 gfx::Screen* screen = gfx::Screen::GetScreenFor(view); 218 if (screen->IsDIPEnabled()) { 219 gfx::Display display = screen->GetDisplayNearestWindow(view); 220 return GetScaleFactorFromScale(display.device_scale_factor()); 221 } 222 return ui::SCALE_FACTOR_100P; 223 } 224 #endif // !defined(OS_MACOSX) 225 226 } // namespace ui 227