Commit 8926b625 authored by Malay Keshav's avatar Malay Keshav Committed by Commit Bot

Implements a custom slider for display size setting

This patch implements a custom slider that provides the basic
functionality of the paper-slider but provides an additional option to
display a label for the current slider value. The slider has the Prefs
Behavior integrated with it.

This slider has a general implemention and is ready to be used in other
locations as well.

Functionalities implemented:
 - Slider works the same way as the paper-slider in terms of mouse and
   keyboard interactions.
 - Additionally The slider displays a label on mouse hover and keyboard
   focus.
 - The slider only works for discrete values or options.

This patch also adds web ui tests for all the slider functionalities.

A video of this slider in use can be found on the bug link below.

Bug: 798906
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: I55fa98eb438ed1c48b4f40c14d8c05085e7a45b4
Component: Display Slider, display settings, webui tests
Reviewed-on: https://chromium-review.googlesource.com/985127
Commit-Queue: Malay Keshav <malaykeshav@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#547101}
parent 18fd458b
......@@ -3703,6 +3703,9 @@
<message name="IDS_SETTINGS_DISPLAY_ZOOM_TITLE" desc="In Device Settings > Displays, the title for the section for changing the display's zoom.">
Display Size
</message>
<message name="IDS_SETTINGS_DISPLAY_ZOOM_SUBLABEL" desc="In Device Settings > Displays, the text describing the display's zoom.">
Make items on your screen smaller or larger
</message>
<message name="IDS_SETTINGS_DISPLAY_ZOOM_VALUE" desc="The currently selected display zoom percentage.">
<ph name="DISPLAY_ZOOM">$1<ex>120</ex>%</ph>
</message>
......
......@@ -65,7 +65,8 @@
'<(EXTERNS_GYP):settings_private',
'<(EXTERNS_GYP):system_display',
'<(INTERFACES_GYP):system_display_interface',
'display_layout'
'display_layout',
'display_size_slider'
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
......@@ -117,6 +118,19 @@
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
'target_name': 'display_size_slider',
'dependencies': [
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-progress/compiled_resources2.gyp:paper-progress-extracted',
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior/compiled_resources2.gyp:iron-a11y-keys-behavior-extracted',
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/compiled_resources2.gyp:iron-resizable-behavior-extracted',
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-range-behavior/compiled_resources2.gyp:iron-range-behavior-extracted',
'<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-behaviors/compiled_resources2.gyp:paper-inky-focus-behavior-extracted',
'<(DEPTH)/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp:cr_policy_pref_behavior',
'../prefs/compiled_resources2.gyp:prefs_behavior',
],
'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
},
{
'target_name': 'power',
'dependencies': [
......
......@@ -12,6 +12,7 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-tabs/paper-tabs.html">
<link rel="import" href="display_layout.html">
<link rel="import" href="display_overscan_dialog.html">
<link rel="import" href="display_size_slider.html">
<link rel="import" href="night_light_slider.html">
<link rel="import" href="../controls/settings_dropdown_menu.html">
<link rel="import" href="../controls/settings_slider.html">
......@@ -161,16 +162,13 @@
<div class="settings-box indented two-line">
<div class="start text-area layout vertical">
<div>$i18n{displayZoomTitle}</div>
<div class="secondary self-start">
[[getDisplayZoomText_(selectedDisplay, selectedZoomPref_.value)]]
</div>
<div class="secondary self-start">$i18n{displayZoomSublabel}</div>
</div>
<settings-slider tick-values="[[zoomValues_]]"
disabled="[[!enableDisplayZoomSlider_(selectedDisplay)]]"
pref="{{selectedZoomPref_}}" on-change="onSelectedZoomChange_"
label-min="$i18n{displaySizeSliderMinLabel}"
label-max="$i18n{displaySizeSliderMaxLabel}">
</settings-slider>
<display-size-slider id="displaySizeSlider"
ticks="[[zoomValues_]]" pref="{{selectedZoomPref_}}"
min-label="$i18n{displaySizeSliderMinLabel}"
max-label="$i18n{displaySizeSliderMaxLabel}">
</display-size-slider>
</div>
<!-- Drop down select menu for resolution -->
......
......@@ -102,7 +102,7 @@ Polymer({
/** @private {!Array<number>} Mode index values for slider. */
modeValues_: Array,
/** @private {!Array<number>} Display zoom percentage values for slider */
/** @private {SliderTicks} Display zoom slider tick values. */
zoomValues_: Array,
/** @private {!DropdownMenuOptionList} */
......@@ -183,6 +183,7 @@ Polymer({
'updateNightLightScheduleSettings_(prefs.ash.night_light.schedule_type.*,' +
' prefs.ash.night_light.enabled.*)',
'onSelectedModeChange_(selectedModePref_.value)',
'onSelectedZoomChange_(selectedZoomPref_.value)',
],
/** @private {number} Selected mode index received from chrome. */
......@@ -315,32 +316,35 @@ Polymer({
*/
getSelectedDisplayZoom_: function(selectedDisplay) {
const selectedZoom = selectedDisplay.displayZoomFactor * 100;
let closestMatch = this.zoomValues_[0];
let closestMatch = this.zoomValues_[0].value;
let minimumDiff = Math.abs(closestMatch - selectedZoom);
for (let i = 0; i < this.zoomValues_.length; i++) {
const currentDiff = Math.abs(this.zoomValues_[i] - selectedZoom);
const currentDiff = Math.abs(this.zoomValues_[i].value - selectedZoom);
if (currentDiff < minimumDiff) {
closestMatch = this.zoomValues_[i];
closestMatch = this.zoomValues_[i].value;
minimumDiff = currentDiff;
}
}
return closestMatch;
return /** @type {number} */ (closestMatch);
},
/**
* Given the display with the current display mode, this function lists all
* the display zoom values in percentage.
* the display zoom values and their labels to be used by the slider.
* @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay
* @return {!Array<number>}
* @return {SliderTicks}
*/
getZoomValues_: function(selectedDisplay) {
/** @type {SliderTicks} */
let zoomValues = [];
for (let i = 0; i < selectedDisplay.availableDisplayZoomFactors.length;
i++) {
zoomValues.push(
Math.round(selectedDisplay.availableDisplayZoomFactors[i] * 100));
const value =
Math.round(selectedDisplay.availableDisplayZoomFactors[i] * 100);
const label = this.i18n('displayZoomValue', value.toString());
zoomValues.push({value: value, label: label});
}
return zoomValues;
},
......@@ -564,16 +568,6 @@ Polymer({
return this.i18n('displayResolutionText', widthStr, heightStr);
},
/**
* @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay
* @return {string}
* @private
*/
getDisplayZoomText_: function(selectedDisplay) {
return this.i18n(
'displayZoomValue', this.selectedZoomPref_.value.toString());
},
/**
* @param {!{detail: string}} e |e.detail| is the id of the selected display.
* @private
......@@ -663,10 +657,14 @@ Polymer({
* @private
*/
onSelectedZoomChange_: function() {
if (this.currentSelectedModeIndex_ == -1 || !this.selectedDisplay)
return;
/** @type {!chrome.system.display.DisplayProperties} */ const properties = {
displayZoomFactor:
/** @type {number} */ (this.selectedZoomPref_.value) / 100.0
};
settings.display.systemDisplayApi.setDisplayProperties(
this.selectedDisplay.id, properties,
this.setPropertiesCallback_.bind(this));
......
<link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-behaviors/paper-inky-focus-behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-range-behavior/iron-range-behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-resizable-behavior/iron-resizable-behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
<dom-module id="display-size-slider">
<template>
<style>
:host {
/* Counteract the margin on #sliderContainer and match the margin from
settings-slider.html */
-webkit-margin-end: -16px;
cursor: default;
display: inline-flex;
font-weight: 500;
min-width: 200px;
text-align: center;
user-select: none;
}
/* focus shows the ripple */
:host(:focus) {
outline: none;
}
:host-context([dir='rtl']) #sliderContainer {
transform: scaleX(-1);
}
/* We dont want the text to be flipped in rtl */
:host-context([dir='rtl']) #labelText,
:host-context([dir='rtl']) #subLabelContainer {
transform: scaleX(-1);
}
#sliderContainer {
display: inline-table;
height: 32px;
margin-left: 16px;
margin-right: 16px;
position: relative;
width: 100%;
}
#sliderContainer:focus {
outline: 0;
}
#labelContainer {
bottom: 36px;
display: none;
height: 1.75em;
position: absolute;
width: inherit;
z-index: 10;
}
/* Show the label on keyboard focus and mouse hover */
.dragging #labelContainer,
:host(:focus) #labelContainer,
#sliderContainer:hover #labelContainer,
.hover #labelContainer {
display: block;
}
.label {
background: var(--google-blue-700);
border-radius: 14px;
color: white;
font-size: 12px;
left: 0;
line-height: 1.5em;
padding: 0 8px;
position: absolute;
text-align: center;
transform: translateX(-50%);
transition: margin-top 200ms cubic-bezier(0, 0, 0.2, 1);
vertical-align: middle;
width: auto;
}
.bar-container {
bottom: 0;
left: 0;
overflow: hidden;
position: absolute;
right: 0;
top: 0;
}
.slider-markers {
box-sizing: border-box;
height: 2px;
left: 0;
pointer-events: none;
position: absolute;
right: -1px;
top: 15px;
@apply --layout-horizontal;
}
.slider-marker {
@apply --layout-flex;
}
.slider-markers::after,
.slider-marker::after {
background-color: #000;
border-radius: 50%;
content: '';
display: block;
height: 2px;
margin-left: -1px;
width: 2px;
}
#sliderBar {
--paper-progress-container-color: var(--paper-grey-400);
--paper-progress-height: 2px;
background-color: transparent;
padding: 15px 0;
width: 100%;
}
.slider-knob {
height: 32px;
left: 0;
margin-left: -16px;
position: absolute;
top: 0;
width: 32px;
}
.slider-knob:focus {
outline: none;
}
.slider-knob-inner {
background-color: var(--google-blue-700);
border: 2px solid var(--google-blue-700);
border-radius: 50%;
box-sizing: border-box;
height: calc(100% - 20px);
margin: 10px;
transition-duration: 180ms;
transition-property: transform, background-color, border;
transition-timing-function: ease;
width: calc(100% - 20px);
}
.expand > .slider-knob > .slider-knob-inner {
transform: scale(1.5);
}
paper-ripple {
color: var(--google-blue-700);
}
#subLabelContainer {
display: flex;
flex-direction: row;
justify-content: space-between;
padding-top: 24px;
pointer-events: none;
}
#subLabelContainer > div {
font-size: 12px;
font-weight: normal;
}
paper-progress {
--paper-progress-active-color: var(--google-blue-700);
--paper-progress-disabled-active-color: var(--paper-grey-400);
}
/* Styles for disabled state */
#sliderContainer.disabled {
pointer-events: none;
}
.disabled > .slider-knob > .slider-knob-inner {
transform: scale3d(0.75, 0.75, 1);
}
#subLabelContainer[disabled] {
color: var(--paper-grey-400);
}
</style>
<div id="sliderContainer"
class$="[[getClassNames_(expand, disabled, dragging)]]"
on-mouseout="onMouseOut_">
<div id="labelContainer" hidden$="[[shouldShowLabel_(ticks, index)]]"
aria-label="[[getLabelForIndex_(ticks, index)]]">
<div id="label" class="label">
<div id="labelText">
[[getLabelForIndex_(ticks, index)]]
</div>
</div>
</div>
<div class="bar-container">
<paper-progress id="sliderBar" disabled$="[[disabled]]"
on-down="onBarDown_" on-up="onBarUp_" on-track="knobTrack_"
value="[[index]]" min="[[min]]" max="[[max]]">
</paper-progress>
</div>
<template is="dom-if" if="[[!disabled]]">
<div class="slider-markers">
<template is="dom-repeat" items="[[markers]]">
<div class="slider-marker"></div>
</template>
</div>
</template>
<div id="sliderKnob" class="slider-knob" on-down="knobDown_"
on-up="resetKnob_" on-track="knobTrack_">
<div class="slider-knob-inner"></div>
</div>
<div id="subLabelContainer" disabled$="[[disabled]]">
<div id="subLabelMin">[[minLabel]]</div>
<div id="subLabelMax">[[maxLabel]]</div>
</div>
</div>
</template>
<script src="display_size_slider.js"></script>
</dom-module>
......@@ -548,6 +548,12 @@
<structure name="IDR_SETTINGS_DEVICE_NIGHT_LIGHT_SLIDER_JS"
file="device_page/night_light_slider.js"
type="chrome_html" />
<structure name="IDR_SETTINGS_DEVICE_DISPLAY_SIZE_SLIDER_HTML"
file="device_page/display_size_slider.html"
type="chrome_html" />
<structure name="IDR_SETTINGS_DEVICE_DISPLAY_SIZE_SLIDER_JS"
file="device_page/display_size_slider.js"
type="chrome_html" />
</if>
<structure name="IDR_SETTINGS_DOWNLOADS_BROWSER_PROXY_HTML"
......
......@@ -684,6 +684,7 @@ void AddDeviceStrings(content::WebUIDataSource* html_source) {
{"displayResolutionSublabel", IDS_SETTINGS_DISPLAY_RESOLUTION_SUBLABEL},
{"displayResolutionMenuItem", IDS_SETTINGS_DISPLAY_RESOLUTION_MENU_ITEM},
{"displayZoomTitle", IDS_SETTINGS_DISPLAY_ZOOM_TITLE},
{"displayZoomSublabel", IDS_SETTINGS_DISPLAY_ZOOM_SUBLABEL},
{"displayZoomValue", IDS_SETTINGS_DISPLAY_ZOOM_VALUE},
{"displaySizeSliderMinLabel", IDS_SETTINGS_DISPLAY_ZOOM_SLIDER_MINIMUM},
{"displaySizeSliderMaxLabel", IDS_SETTINGS_DISPLAY_ZOOM_SLIDER_MAXIMUM},
......
......@@ -1874,3 +1874,28 @@ CrSettingsOnStartupPageTest.prototype = {
TEST_F('CrSettingsOnStartupPageTest', 'All', function() {
mocha.run();
});
GEN('#if defined(OS_CHROMEOS)');
/**
* @constructor
* @extends {CrSettingsBrowserTest}
*/
function CrSettingsDisplaySizeSliderTest() {}
CrSettingsDisplaySizeSliderTest.prototype = {
__proto__: CrSettingsBrowserTest.prototype,
/** @override */
browsePreload: 'chrome://settings/device_page/display_size_slider.html',
/** @override */
extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
'display_size_slider_test.js',
]),
};
TEST_F('CrSettingsDisplaySizeSliderTest', 'All', function() {
mocha.run();
});
GEN('#endif // defined(OS_CHROMEOS)');
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment