Diff
Modified: trunk/LayoutTests/ChangeLog (259169 => 259170)
--- trunk/LayoutTests/ChangeLog 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/LayoutTests/ChangeLog 2020-03-29 02:56:05 UTC (rev 259170)
@@ -1,3 +1,13 @@
+2020-03-28 Devin Rousso <[email protected]>
+
+ Web Inspector: CSS: create visual editor for `box-shadow`
+ https://bugs.webkit.org/show_bug.cgi?id=208380
+
+ Reviewed by Timothy Hatcher.
+
+ * inspector/model/boxShadow.html: Added.
+ * inspector/model/boxShadow-expected.txt: Added.
+
2020-03-27 Simon Fraser <[email protected]>
Sideways jiggles when scrolling the shelves on beta.music.apple.com
Added: trunk/LayoutTests/inspector/model/boxShadow-expected.txt (0 => 259170)
--- trunk/LayoutTests/inspector/model/boxShadow-expected.txt (rev 0)
+++ trunk/LayoutTests/inspector/model/boxShadow-expected.txt 2020-03-29 02:56:05 UTC (rev 259170)
@@ -0,0 +1,80 @@
+Tests for the WI.BoxShadow model object.
+
+
+== Running test suite: WI.BoxShadow
+-- Running test case: WI.BoxShadow.fromString
+PASS: default value should be "none"
+
+"1px 2rem" resolves to "1px 2rem"
+"red 1px 2rem" resolves to "1px 2rem red"
+"1px red 2rem" resolves to "1px 2rem red"
+"1px 2rem red" resolves to "1px 2rem red"
+"inset 1px 2rem" resolves to "1px 2rem inset"
+"1px inset 2rem" resolves to "1px 2rem inset"
+"1px 2rem inset" resolves to "1px 2rem inset"
+"inset 1px 2rem red" resolves to "1px 2rem red inset"
+"1px inset 2rem red" resolves to "1px 2rem red inset"
+"1px 2rem inset red" resolves to "1px 2rem red inset"
+"1px 2rem red inset" resolves to "1px 2rem red inset"
+
+"1px 2rem 3in" resolves to "1px 2rem 3in"
+"red 1px 2rem 3in" resolves to "1px 2rem 3in red"
+"1px red 2rem 3in" resolves to "1px 2rem 3in red"
+"1px 2rem red 3in" resolves to "1px 2rem 3in red"
+"1px 2rem 3in red" resolves to "1px 2rem 3in red"
+"inset 1px 2rem 3in" resolves to "1px 2rem 3in inset"
+"1px inset 2rem 3in" resolves to "1px 2rem 3in inset"
+"1px 2rem inset 3in" resolves to "1px 2rem 3in inset"
+"1px 2rem 3in inset" resolves to "1px 2rem 3in inset"
+"inset 1px 2rem 3in red" resolves to "1px 2rem 3in red inset"
+"1px inset 2rem 3in red" resolves to "1px 2rem 3in red inset"
+"1px 2rem inset 3in red" resolves to "1px 2rem 3in red inset"
+"1px 2rem 3in inset red" resolves to "1px 2rem 3in red inset"
+"1px 2rem 3in red inset" resolves to "1px 2rem 3in red inset"
+
+"1px 2rem 3in 4q" resolves to "1px 2rem 3in 4q"
+"red 1px 2rem 3in 4q" resolves to "1px 2rem 3in 4q red"
+"1px red 2rem 3in 4q" resolves to "1px 2rem 3in 4q red"
+"1px 2rem red 3in 4q" resolves to "1px 2rem 3in 4q red"
+"1px 2rem 3in red 4q" resolves to "1px 2rem 3in 4q red"
+"1px 2rem 3in 4q red" resolves to "1px 2rem 3in 4q red"
+"inset 1px 2rem 3in 4q" resolves to "1px 2rem 3in 4q inset"
+"1px inset 2rem 3in 4q" resolves to "1px 2rem 3in 4q inset"
+"1px 2rem inset 3in 4q" resolves to "1px 2rem 3in 4q inset"
+"1px 2rem 3in inset 4q" resolves to "1px 2rem 3in 4q inset"
+"1px 2rem 3in 4q inset" resolves to "1px 2rem 3in 4q inset"
+"inset 1px 2rem 3in 4q red" resolves to "1px 2rem 3in 4q red inset"
+"1px inset 2rem 3in 4q red" resolves to "1px 2rem 3in 4q red inset"
+"1px 2rem inset 3in 4q red" resolves to "1px 2rem 3in 4q red inset"
+"1px 2rem 3in inset 4q red" resolves to "1px 2rem 3in 4q red inset"
+"1px 2rem 3in 4q inset red" resolves to "1px 2rem 3in 4q red inset"
+"1px 2rem 3in 4q red inset" resolves to "1px 2rem 3in 4q red inset"
+
+"0 0 0 0 red inset" resolves to "0 0 0 0 red inset"
+"0px 0px 0px 0px red inset" resolves to "0 0 0 0 red inset"
+"0rem 0rem 0rem 0rem red inset" resolves to "0 0 0 0 red inset"
+"0in 0in 0in 0in red inset" resolves to "0 0 0 0 red inset"
+"0q 0q 0q 0q red inset" resolves to "0 0 0 0 red inset"
+
+"none" resolves to "none"
+
+"1px 2rem 3in 4q rgb(11, 12, 13) inset" resolves to "1px 2rem 3in 4q rgb(11, 12, 13) inset"
+
+PASS: '1' should not be detected
+
+PASS: '1%' should not be detected
+PASS: '1px 2%' should not be detected
+PASS: '1px 2px 3%' should not be detected
+PASS: '1px 2px 3px 4%' should not be detected
+
+PASS: '1px' should not be detected
+PASS: '1px 2rem 3in 4q 5pt' should not be detected
+PASS: '1px 2rem 3in 4q invalid' should not be detected
+PASS: '1px 2rem 3in 4q red inset extra' should not be detected
+PASS: 'red' should not be detected
+PASS: 'inset' should not be detected
+
+PASS: 'red red' should not be detected
+PASS: 'inset inset' should not be detected
+
+
Added: trunk/LayoutTests/inspector/model/boxShadow.html (0 => 259170)
--- trunk/LayoutTests/inspector/model/boxShadow.html (rev 0)
+++ trunk/LayoutTests/inspector/model/boxShadow.html 2020-03-29 02:56:05 UTC (rev 259170)
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script>
+function test()
+{
+ let suite = InspectorTest.createSyncSuite("WI.BoxShadow");
+
+ suite.addTestCase({
+ name: "WI.BoxShadow.fromString",
+ description: "Test we can detect box shadows from strings.",
+ test() {
+ InspectorTest.expectEqual((new WI.BoxShadow).toString(), "none", `default value should be "none"`);
+ InspectorTest.newline();
+
+ function testGood(string) {
+ let boxShadow = WI.BoxShadow.fromString(string);
+ InspectorTest.assert(boxShadow instanceof WI.BoxShadow, `'${string}' should be detected`);
+ if (boxShadow)
+ InspectorTest.log(`"${string}" resolves to "${boxShadow.toString()}"`);
+ }
+
+ // offsetX and offsetY
+ testGood("1px 2rem");
+ testGood("red 1px 2rem");
+ testGood("1px red 2rem");
+ testGood("1px 2rem red");
+ testGood("inset 1px 2rem");
+ testGood("1px inset 2rem");
+ testGood("1px 2rem inset");
+ testGood("inset 1px 2rem red");
+ testGood("1px inset 2rem red");
+ testGood("1px 2rem inset red");
+ testGood("1px 2rem red inset");
+ InspectorTest.newline();
+
+ // blurRadius
+ testGood("1px 2rem 3in");
+ testGood("red 1px 2rem 3in");
+ testGood("1px red 2rem 3in");
+ testGood("1px 2rem red 3in");
+ testGood("1px 2rem 3in red");
+ testGood("inset 1px 2rem 3in");
+ testGood("1px inset 2rem 3in");
+ testGood("1px 2rem inset 3in");
+ testGood("1px 2rem 3in inset");
+ testGood("inset 1px 2rem 3in red");
+ testGood("1px inset 2rem 3in red");
+ testGood("1px 2rem inset 3in red");
+ testGood("1px 2rem 3in inset red");
+ testGood("1px 2rem 3in red inset");
+ InspectorTest.newline();
+
+ // spreadRadius
+ testGood("1px 2rem 3in 4q");
+ testGood("red 1px 2rem 3in 4q");
+ testGood("1px red 2rem 3in 4q");
+ testGood("1px 2rem red 3in 4q");
+ testGood("1px 2rem 3in red 4q");
+ testGood("1px 2rem 3in 4q red");
+ testGood("inset 1px 2rem 3in 4q");
+ testGood("1px inset 2rem 3in 4q");
+ testGood("1px 2rem inset 3in 4q");
+ testGood("1px 2rem 3in inset 4q");
+ testGood("1px 2rem 3in 4q inset");
+ testGood("inset 1px 2rem 3in 4q red");
+ testGood("1px inset 2rem 3in 4q red");
+ testGood("1px 2rem inset 3in 4q red");
+ testGood("1px 2rem 3in inset 4q red");
+ testGood("1px 2rem 3in 4q inset red");
+ testGood("1px 2rem 3in 4q red inset");
+ InspectorTest.newline();
+
+ // 0 ignores unit
+ testGood("0 0 0 0 red inset");
+ testGood("0px 0px 0px 0px red inset");
+ testGood("0rem 0rem 0rem 0rem red inset");
+ testGood("0in 0in 0in 0in red inset");
+ testGood("0q 0q 0q 0q red inset");
+ InspectorTest.newline();
+
+ // keywords
+ testGood("none");
+ InspectorTest.newline();
+
+ // color with parenthesis
+ testGood("1px 2rem 3in 4q rgb(11, 12, 13) inset");
+ InspectorTest.newline();
+
+ function testBad(string) {
+ let boxShadow = WI.BoxShadow.fromString(string);
+ InspectorTest.expectNull(boxShadow, `'${string}' should not be detected`);
+ }
+
+ // missing unit
+ testBad("1");
+ InspectorTest.newline();
+
+ // invalid unit
+ testBad("1%");
+ testBad("1px 2%");
+ testBad("1px 2px 3%");
+ testBad("1px 2px 3px 4%");
+ InspectorTest.newline();
+
+ // missing or extra components
+ testBad("1px");
+ testBad("1px 2rem 3in 4q 5pt");
+ testBad("1px 2rem 3in 4q invalid");
+ testBad("1px 2rem 3in 4q red inset extra");
+ testBad("red");
+ testBad("inset");
+ InspectorTest.newline();
+
+ // duplicate components
+ testBad("red red");
+ testBad("inset inset");
+ InspectorTest.newline();
+
+ return true;
+ }
+ });
+
+ suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+<p>Tests for the WI.BoxShadow model object.</p>
+</body>
+</html>
Modified: trunk/Source/WebInspectorUI/ChangeLog (259169 => 259170)
--- trunk/Source/WebInspectorUI/ChangeLog 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/Source/WebInspectorUI/ChangeLog 2020-03-29 02:56:05 UTC (rev 259170)
@@ -1,5 +1,120 @@
2020-03-28 Devin Rousso <[email protected]>
+ Web Inspector: CSS: create visual editor for `box-shadow`
+ https://bugs.webkit.org/show_bug.cgi?id=208380
+
+ Reviewed by Timothy Hatcher.
+
+ Recognize `box-shadow` CSS properties in the Styles sidebar, parse the comma-separated list
+ value for individual box shadows, and create a `WI.InlineSwatch` for each. When clicked,
+ show a `WI.Popover` with a `WI.BoxShadowEditor`, which contains a table of editors:
+
+ Offset X | <input type="text"> | [ 2D (X & Y) ]
+ Offset Y | <input type="text"> | [ Slider ]
+ Inset | <input type="checkbox"> |
+ Blur | <input type="text"> | <input type="range">
+ Spread | <input type="text"> | <input type="range">
+ [ ]
+ [ ]
+ [ full color picker ]
+ [ ]
+ [ ]
+
+
+ * UserInterface/Models/BoxShadow.js: Added.
+ (WI.BoxShadow):
+ (WI.BoxShadow.fromString):
+ (WI.BoxShadow.parseNumberComponent):
+ (WI.BoxShadow.prototype.get offsetX):
+ (WI.BoxShadow.prototype.get offsetY):
+ (WI.BoxShadow.prototype.get blurRadius):
+ (WI.BoxShadow.prototype.get spreadRadius):
+ (WI.BoxShadow.prototype.get inset):
+ (WI.BoxShadow.prototype.get color):
+ (WI.BoxShadow.prototype.copy):
+ (WI.BoxShadow.prototype.toString):
+ (WI.BoxShadow.prototype.toString.stringifyNumberComponent):
+
+ * UserInterface/Models/CSSCompletions.js:
+ Add a `Set` of allowed CSS length units.
+
+ * UserInterface/Views/BoxShadowEditor.js: Added.
+ (WI.BoxShadowEditor):
+ (WI.BoxShadowEditor.createInputRow):
+ (WI.BoxShadowEditor.createSlider):
+ (WI.BoxShadowEditor.prototype.get element):
+ (WI.BoxShadowEditor.prototype.get boxShadow):
+ (WI.BoxShadowEditor.prototype.set boxShadow):
+ (WI.BoxShadowEditor.prototype.handleEvent):
+ (WI.BoxShadowEditor.prototype._updateBoxShadow):
+ (WI.BoxShadowEditor.prototype._updateBoxShadowOffsetFromSliderMouseEvent):
+ (WI.BoxShadowEditor.prototype._determineShiftForEvent):
+ (WI.BoxShadowEditor.prototype._handleOffsetSliderSVGKeyDown):
+ (WI.BoxShadowEditor.prototype._handleOffsetSliderSVGMouseDown):
+ (WI.BoxShadowEditor.prototype._handleWindowMouseMove):
+ (WI.BoxShadowEditor.prototype._handleWindowMouseUp):
+ (WI.BoxShadowEditor.prototype._handleOffsetXInputInput):
+ (WI.BoxShadowEditor.prototype._handleOffsetXInputKeyDown):
+ (WI.BoxShadowEditor.prototype._handleOffsetYInputInput):
+ (WI.BoxShadowEditor.prototype._handleOffsetYInputKeyDown):
+ (WI.BoxShadowEditor.prototype._handleBlurRadiusInputInput):
+ (WI.BoxShadowEditor.prototype._handleBlurRadiusInputKeyDown):
+ (WI.BoxShadowEditor.prototype._handleBlurRadiusSliderInput):
+ (WI.BoxShadowEditor.prototype._handleSpreadRadiusInputInput):
+ (WI.BoxShadowEditor.prototype._handleSpreadRadiusInputKeyDown):
+ (WI.BoxShadowEditor.prototype._handleSpreadRadiusSliderInput):
+ (WI.BoxShadowEditor.prototype._handleInsetCheckboxChange):
+ (WI.BoxShadowEditor.prototype._handleColorChanged):
+ * UserInterface/Views/BoxShadowEditor.css: Added.
+ (.box-shadow-editor):
+ (.box-shadow-editor > table):
+ (.box-shadow-editor > table > tr > th):
+ (.box-shadow-editor > table > tr > td):
+ (.box-shadow-editor > table > tr > td > input[type="text"]):
+ (.box-shadow-editor > table > tr > td > input[type="range"]):
+ (.box-shadow-editor > table > tr > td > svg):
+ (.box-shadow-editor > table > tr > td > svg line.axis):
+ (.box-shadow-editor > table > tr > td > svg line:not(.axis)):
+ (.box-shadow-editor > table > tr > td > svg circle):
+ (@media (prefers-color-scheme: dark) .box-shadow-editor > table > tr > th):
+
+ * UserInterface/Views/InlineSwatch.js:
+ (WI.InlineSwatch):
+ (WI.InlineSwatch.prototype._fallbackValue):
+ (WI.InlineSwatch.prototype._valueEditorValueDidChange):
+ * UserInterface/Views/InlineSwatch.css:
+ (.inline-swatch):
+ (.inline-swatch:not(.box-shadow), .inline-swatch.box-shadow:matches(:hover, :active)): Added.
+ (.inline-swatch:matches(.bezier, .box-shadow, .spring, .variable)): Added.
+ (.inline-swatch:not(.read-only):matches(.bezier, .box-shadow, .spring, .variable):hover): Added.
+ (.inline-swatch:not(.read-only):matches(.bezier, .box-shadow, .spring, .variable):active): Added.
+ (.inline-swatch:matches(.bezier, .box-shadow, .spring, .variable) > span): Added.
+ (@media (prefers-color-scheme: dark) .inline-swatch.box-shadow > svg): Added.
+ (.inline-swatch:not(.read-only):matches(.bezier, .spring, .variable):hover): Deleted.
+ (.inline-swatch:not(.read-only):matches(.bezier, .spring, .variable):active): Deleted.
+ (.inline-swatch:matches(.bezier, .spring, .variable) > span): Deleted.
+
+ * UserInterface/Views/SpreadsheetStyleProperty.js:
+ (WI.SpreadsheetStyleProperty.prototype._replaceSpecialTokens):
+ (WI.SpreadsheetStyleProperty.prototype._addGradientTokens):
+ (WI.SpreadsheetStyleProperty.prototype._addColorTokens):
+ (WI.SpreadsheetStyleProperty.prototype._addTimingFunctionTokens):
+ (WI.SpreadsheetStyleProperty.prototype._addBoxShadowTokens):
+ (WI.SpreadsheetStyleProperty.prototype._resolveVariables):
+
+ * UserInterface/Views/Variables.css:
+ (:root):
+ * UserInterface/Views/ColorPicker.css:
+ (.color-picker):
+ Move `--color-picker-width` to `:root` so that `WI.BoxShadowEditor` can use it.
+
+ * UserInterface/Main.html:
+ * UserInterface/Test.html:
+ * Localizations/en.lproj/localizedStrings.js:
+ * UserInterface/Images/BoxShadow.svg: Added.
+
+2020-03-28 Devin Rousso <[email protected]>
+
Web Inspector: REGRESSION(r257759): Network: graph in Timing pane of selected resource is missing bars
https://bugs.webkit.org/show_bug.cgi?id=209525
Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (259169 => 259170)
--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2020-03-29 02:56:05 UTC (rev 259170)
@@ -192,6 +192,8 @@
localizedStrings["Blackbox Script"] = "Blackbox Script";
localizedStrings["Blackbox script to ignore it when debugging"] = "Blackbox script to ignore it when debugging";
localizedStrings["Block Variables"] = "Block Variables";
+/* Input label for the blur radius of a CSS box shadow */
+localizedStrings["Blur @ Box Shadow Editor"] = "Blur";
localizedStrings["Body:"] = "Body:";
localizedStrings["Boundary"] = "Boundary";
localizedStrings["Box Model"] = "Box Model";
@@ -448,6 +450,7 @@
localizedStrings["Edit Breakpoint\u2026"] = "Edit Breakpoint\u2026";
localizedStrings["Edit Local Override\u2026"] = "Edit Local Override\u2026";
localizedStrings["Edit \u201C%s\u201D"] = "Edit \u201C%s\u201D";
+localizedStrings["Edit \u201Cbox-shadow\u201D"] = "Edit \u201Cbox-shadow\u201D";
localizedStrings["Edit \u201Ccubic-bezier\u201D function"] = "Edit \u201Ccubic-bezier\u201D function";
localizedStrings["Edit \u201Cspring\u201D function"] = "Edit \u201Cspring\u201D function";
localizedStrings["Edit configuration"] = "Edit configuration";
@@ -679,6 +682,8 @@
localizedStrings["Initiated"] = "Initiated";
localizedStrings["Initiator"] = "Initiator";
localizedStrings["Input: "] = "Input: ";
+/* Checkbox label for the inset of a CSS box shadow. */
+localizedStrings["Inset @ Box Shadow Editor"] = "Inset";
localizedStrings["Inspector Bootstrap Script"] = "Inspector Bootstrap Script";
localizedStrings["Inspector Override"] = "Inspector Override";
localizedStrings["Inspector Style Sheet"] = "Inspector Style Sheet";
@@ -846,6 +851,10 @@
localizedStrings["Observer Handlers:"] = "Observer Handlers:";
localizedStrings["Observers:"] = "Observers:";
localizedStrings["Off"] = "Off";
+/* Input label for the x-axis of the offset of a CSS box shadow */
+localizedStrings["Offset X @ Box Shadow Editor"] = "Offset X";
+/* Input label for the y-axis of the offset of a CSS box shadow */
+localizedStrings["Offset Y @ Box Shadow Editor"] = "Offset Y";
localizedStrings["Once"] = "Once";
localizedStrings["Online"] = "Online";
localizedStrings["Only show resources with issues"] = "Only show resources with issues";
@@ -1148,6 +1157,8 @@
localizedStrings["Specificity: (%d, %d, %d)"] = "Specificity: (%d, %d, %d)";
localizedStrings["Specificity: No value for selected element"] = "Specificity: No value for selected element";
localizedStrings["Spelling"] = "Spelling";
+/* Input label for the spread radius of a CSS box shadow */
+localizedStrings["Spread @ Box Shadow Editor"] = "Spread";
localizedStrings["Staging:"] = "Staging:";
localizedStrings["Stalled"] = "Stalled";
localizedStrings["Start"] = "Start";
Added: trunk/Source/WebInspectorUI/UserInterface/Images/BoxShadow.svg (0 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Images/BoxShadow.svg (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Images/BoxShadow.svg 2020-03-29 02:56:05 UTC (rev 259170)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2020 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+ <path d="M 4 2 V 4 H 2 V 14 H 12 V 12 H 14 V 2 Z M 5.5 10.5 V 3.5 H 12.5 V 10.5 Z"/>
+</svg>
Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (259169 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Main.html 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html 2020-03-29 02:56:05 UTC (rev 259170)
@@ -43,6 +43,7 @@
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
+ <link rel="stylesheet" href=""
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
<link rel="stylesheet" href=""
@@ -366,6 +367,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
@@ -605,6 +607,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
Added: trunk/Source/WebInspectorUI/UserInterface/Models/BoxShadow.js (0 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Models/BoxShadow.js (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/BoxShadow.js 2020-03-29 02:56:05 UTC (rev 259170)
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.BoxShadow = class BoxShadow
+{
+ constructor(offsetX, offsetY, blurRadius, spreadRadius, inset, color)
+ {
+ console.assert(!offsetX || (typeof offsetX === "object" && !isNaN(offsetX.value) && offsetX.unit), offsetX);
+ console.assert(!offsetY || (typeof offsetY === "object" && !isNaN(offsetY.value) && offsetY.unit), offsetY);
+ console.assert(!blurRadius || (typeof blurRadius === "object" && blurRadius.value >= 0 && blurRadius.unit), blurRadius);
+ console.assert(!spreadRadius || (typeof spreadRadius === "object" && !isNaN(spreadRadius.value) && spreadRadius.unit), spreadRadius);
+ console.assert(!inset || typeof inset === "boolean", inset);
+ console.assert(!color || color instanceof WI.Color, color);
+
+ this._offsetX = offsetX || null;
+ this._offsetY = offsetY || null;
+ this._blurRadius = blurRadius || null;
+ this._spreadRadius = spreadRadius || null;
+ this._inset = !!inset;
+ this._color = color || null;
+ }
+
+ // Static
+
+ static fromString(cssString)
+ {
+ if (cssString === "none")
+ return new WI.BoxShadow;
+
+ let offsetX = null;
+ let offsetY = null;
+ let blurRadius = null;
+ let spreadRadius = null;
+ let inset = false;
+ let color = null;
+
+ let startIndex = 0;
+ let openParentheses = 0;
+ let numberComponentCount = 0;
+ for (let i = 0; i <= cssString.length; ++i) {
+ if (cssString[i] === "(") {
+ ++openParentheses;
+ continue;
+ }
+
+ if (cssString[i] === ")") {
+ --openParentheses;
+ continue;
+ }
+
+ if ((cssString[i] !== " " || openParentheses) && i !== cssString.length)
+ continue;
+
+ let component = cssString.substring(startIndex, i + 1).trim();
+
+ startIndex = i + 1;
+
+ if (!component.length)
+ continue;
+
+ if (component === "inset") {
+ if (inset)
+ return null;
+ inset = true;
+ continue;
+ }
+
+ let possibleColor = WI.Color.fromString(component);
+ if (possibleColor) {
+ if (color)
+ return null;
+ color = possibleColor;
+ continue;
+ }
+
+ let numberComponent = WI.BoxShadow.parseNumberComponent(component);
+ if (!numberComponent)
+ return null;
+
+ switch (++numberComponentCount) {
+ case 1:
+ offsetX = numberComponent;
+ break;
+
+ case 2:
+ offsetY = numberComponent;
+ break;
+
+ case 3:
+ blurRadius = numberComponent;
+ if (blurRadius.value < 0)
+ return null;
+ break;
+
+ case 4:
+ spreadRadius = numberComponent;
+ break;
+
+ default:
+ return null;
+ }
+ }
+
+ if (!offsetX || !offsetY)
+ return null;
+
+ return new WI.BoxShadow(offsetX, offsetY, blurRadius, spreadRadius, inset, color);
+ }
+
+ static parseNumberComponent(string)
+ {
+ let value = parseFloat(string);
+ if (isNaN(value))
+ return null;
+
+ let unit = string.replace(value, "");
+ if (!unit) {
+ if (value)
+ return null;
+ unit = "px";
+ } else if (!WI.CSSCompletions.lengthUnits.has(unit))
+ return null;
+
+ return {unit, value};
+ }
+
+ // Public
+
+ get offsetX() { return this._offsetX; }
+ get offsetY() { return this._offsetY; }
+ get blurRadius() { return this._blurRadius; }
+ get spreadRadius() { return this._spreadRadius; }
+ get inset() { return this._inset; }
+ get color() { return this._color; }
+
+ copy()
+ {
+ return new WI.BoxShadow(this._offsetX, this._offsetY, this._blurRadius, this._spreadRadius, this._inset, this._color);
+ }
+
+ toString()
+ {
+ if (!this._offsetX || !this._offsetY)
+ return "none";
+
+ function stringifyNumberComponent({value, unit}) {
+ return value + (value ? unit : "");
+ }
+
+ let values = [
+ stringifyNumberComponent(this._offsetX),
+ stringifyNumberComponent(this._offsetY),
+ ];
+
+ if (this._blurRadius)
+ values.push(stringifyNumberComponent(this._blurRadius));
+
+ if (this._spreadRadius)
+ values.push(stringifyNumberComponent(this._spreadRadius));
+
+ if (this._color)
+ values.push(this._color.toString());
+
+ if (this._inset)
+ values.push("inset");
+
+ return values.join(" ");
+ }
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js (259169 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js 2020-03-29 02:56:05 UTC (rev 259170)
@@ -357,3 +357,21 @@
};
WI.CSSCompletions.cssNameCompletions = null;
+
+WI.CSSCompletions.lengthUnits = new Set([
+ "ch",
+ "cm",
+ "em",
+ "ex",
+ "in",
+ "mm",
+ "pc",
+ "pt",
+ "px",
+ "q",
+ "rem",
+ "vh",
+ "vmax",
+ "vmin",
+ "vw",
+]);
Modified: trunk/Source/WebInspectorUI/UserInterface/Test.html (259169 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Test.html 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/Source/WebInspectorUI/UserInterface/Test.html 2020-03-29 02:56:05 UTC (rev 259170)
@@ -125,6 +125,7 @@
<script src=""
<script src=""
+ <script src=""
<script src=""
<script src=""
<script src=""
Added: trunk/Source/WebInspectorUI/UserInterface/Views/BoxShadowEditor.css (0 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Views/BoxShadowEditor.css (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/BoxShadowEditor.css 2020-03-29 02:56:05 UTC (rev 259170)
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.box-shadow-editor {
+ max-width: var(--color-picker-width);
+}
+
+.box-shadow-editor > table {
+ width: 100%;
+ padding: 0 4px;
+ border-spacing: 0;
+}
+
+.box-shadow-editor > table > tr > th {
+ font-weight: bold;
+ text-align: end;
+ line-height: 23px;
+ vertical-align: top;
+ white-space: nowrap;
+ color: hsl(0, 0%, 34%);
+}
+
+.box-shadow-editor > table > tr > td {
+ -webkit-padding-start: 4px;
+}
+
+.box-shadow-editor > table > tr > td > input[type="text"] {
+ width: 100%;
+ padding: 3px 4px 2px;
+ font-family: Menlo, monospace;
+ text-align: end;
+ background-color: var(--background-color-code);
+ border: 1px solid var(--text-color-quaternary);
+ -webkit-appearance: none;
+}
+
+.box-shadow-editor > table > tr > td > input[type="range"] {
+ width: 120px;
+ height: 19px;
+ background-color: transparent;
+}
+
+.box-shadow-editor > table > tr.offset-x > td > svg {
+ -webkit-margin-start: 5px;
+}
+
+.box-shadow-editor > table > tr.offset-x > td > svg line.axis {
+ fill: none;
+ stroke: var(--text-color-quaternary);
+ stroke-width: 2;
+ stroke-linecap: round;
+}
+
+.box-shadow-editor > table > tr.offset-x > td > svg line:not(.axis) {
+ fill: none;
+ stroke: var(--glyph-color-active);
+ stroke-width: 2;
+ stroke-linecap: round;
+}
+
+.box-shadow-editor > table > tr.offset-x > td > svg circle {
+ r: 5px; /* keep in sync with `this._offsetSliderKnobRadius` */
+ fill: var(--glyph-color-active);
+}
+
+.box-shadow-editor > table > tr.inset > td {
+ vertical-align: top;
+}
+
+.box-shadow-editor > table > tr.inset > td > input[type="checkbox"] {
+ margin-top: 0.5em;
+}
+
+@media (prefers-color-scheme: dark) {
+ .box-shadow-editor > table > tr > th {
+ color: var(--text-color-secondary);
+ }
+}
Added: trunk/Source/WebInspectorUI/UserInterface/Views/BoxShadowEditor.js (0 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Views/BoxShadowEditor.js (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/BoxShadowEditor.js 2020-03-29 02:56:05 UTC (rev 259170)
@@ -0,0 +1,524 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.BoxShadowEditor = class BoxShadowEditor extends WI.Object
+{
+ constructor()
+ {
+ super();
+
+ this._element = document.createElement("div");
+ this._element.classList.add("box-shadow-editor");
+
+ let tableElement = this._element.appendChild(document.createElement("table"));
+
+ function createInputRow(identifier, label) {
+ let id = `box-shadow-editor-${identifier}-input`;
+
+ let rowElement = tableElement.appendChild(document.createElement("tr"));
+ rowElement.className = identifier;
+
+ let headerElement = rowElement.appendChild(document.createElement("th"));
+
+ let labelElement = headerElement.appendChild(document.createElement("label"));
+ labelElement.setAttribute("for", id);
+ labelElement.textContent = label;
+
+ let dataElement = rowElement.appendChild(document.createElement("td"));
+
+ let inputElement = dataElement.appendChild(document.createElement("input"));
+ inputElement.id = id;
+ return {rowElement, inputElement};
+ }
+
+ function createSlider(rowElement, min, max) {
+ let dataElement = rowElement.appendChild(document.createElement("td"));
+
+ let rangeElement = dataElement.appendChild(document.createElement("input"));
+ rangeElement.type = "range";
+ rangeElement.min = min;
+ rangeElement.max = max;
+ return rangeElement;
+ }
+
+ let offsetXRow = createInputRow("offset-x", WI.UIString("Offset X", "Offset X @ Box Shadow Editor", "Input label for the x-axis of the offset of a CSS box shadow"));
+
+ this._offsetXInput = offsetXRow.inputElement;
+ this._offsetXInput.type = "text"
+ this._offsetXInput.addEventListener("input", this._handleOffsetXInputInput.bind(this));
+ this._offsetXInput.addEventListener("keydown", this._handleOffsetXInputKeyDown.bind(this));
+
+ let offsetSliderDataElement = offsetXRow.rowElement.appendChild(document.createElement("td"));
+ offsetSliderDataElement.setAttribute("rowspan", 3);
+
+ this._offsetSliderKnobRadius = 5; // keep in sync with `.box-shadow-editor > table > tr > td > svg circle`
+ this._offsetSliderAreaSize = 100;
+
+ const offsetSliderContainerSize = this._offsetSliderAreaSize + (this._offsetSliderKnobRadius * 2);
+
+ this._offsetSliderSVG = offsetSliderDataElement.appendChild(createSVGElement("svg"));
+ this._offsetSliderSVG.setAttribute("tabindex", 0);
+ this._offsetSliderSVG.setAttribute("width", offsetSliderContainerSize);
+ this._offsetSliderSVG.setAttribute("height", offsetSliderContainerSize);
+ this._offsetSliderSVG.addEventListener("mousedown", this);
+ this._offsetSliderSVG.addEventListener("keydown", this);
+
+ this._offsetSliderSVGMouseDownPoint = null;
+
+ let offsetSliderGroup = this._offsetSliderSVG.appendChild(createSVGElement("g"));
+ offsetSliderGroup.setAttribute("transform", `translate(${this._offsetSliderKnobRadius}, ${this._offsetSliderKnobRadius})`);
+
+ let offsetSliderXAxisLine = offsetSliderGroup.appendChild(createSVGElement("line"));
+ offsetSliderXAxisLine.classList.add("axis");
+ offsetSliderXAxisLine.setAttribute("x1", this._offsetSliderAreaSize / 2);
+ offsetSliderXAxisLine.setAttribute("y1", 0);
+ offsetSliderXAxisLine.setAttribute("x2", this._offsetSliderAreaSize / 2);
+ offsetSliderXAxisLine.setAttribute("y2", this._offsetSliderAreaSize);
+
+ let offsetSliderYAxisLine = offsetSliderGroup.appendChild(createSVGElement("line"));
+ offsetSliderYAxisLine.classList.add("axis");
+ offsetSliderYAxisLine.setAttribute("x1", 0);
+ offsetSliderYAxisLine.setAttribute("y1", this._offsetSliderAreaSize / 2);
+ offsetSliderYAxisLine.setAttribute("x2", this._offsetSliderAreaSize);
+ offsetSliderYAxisLine.setAttribute("y2", this._offsetSliderAreaSize / 2);
+
+ this._offsetSliderLine = offsetSliderGroup.appendChild(createSVGElement("line"));
+ this._offsetSliderLine.setAttribute("x1", this._offsetSliderAreaSize / 2);
+ this._offsetSliderLine.setAttribute("y1", this._offsetSliderAreaSize / 2);
+
+ this._offsetSliderKnob = offsetSliderGroup.appendChild(createSVGElement("circle"));
+
+ let offsetYRow = createInputRow("offset-y", WI.UIString("Offset Y", "Offset Y @ Box Shadow Editor", "Input label for the y-axis of the offset of a CSS box shadow"));
+
+ this._offsetYInput = offsetYRow.inputElement;
+ this._offsetYInput.type = "text"
+ this._offsetYInput.addEventListener("input", this._handleOffsetYInputInput.bind(this));
+ this._offsetYInput.addEventListener("keydown", this._handleOffsetYInputKeyDown.bind(this));
+
+ let insetRow = createInputRow("inset", WI.UIString("Inset", "Inset @ Box Shadow Editor", "Checkbox label for the inset of a CSS box shadow."));
+
+ this._insetCheckbox = insetRow.inputElement;
+ this._insetCheckbox.type = "checkbox";
+ this._insetCheckbox.addEventListener("change", this._handleInsetCheckboxChange.bind(this));
+
+ let blurRadiusRow = createInputRow("blur-radius", WI.UIString("Blur", "Blur @ Box Shadow Editor", "Input label for the blur radius of a CSS box shadow"));
+
+ this._blurRadiusInput = blurRadiusRow.inputElement;
+ this._blurRadiusInput.type = "text"
+ this._blurRadiusInput.addEventListener("input", this._handleBlurRadiusInputInput.bind(this));
+ this._blurRadiusInput.addEventListener("keydown", this._handleBlurRadiusInputKeyDown.bind(this));
+ this._blurRadiusInput.min = 0;
+
+ this._blurRadiusSlider = createSlider(blurRadiusRow.rowElement, 0, 100);
+ this._blurRadiusSlider.addEventListener("input", this._handleBlurRadiusSliderInput.bind(this));
+
+ let spreadRadiusRow = createInputRow("spread-radius", WI.UIString("Spread", "Spread @ Box Shadow Editor", "Input label for the spread radius of a CSS box shadow"));
+
+ this._spreadRadiusInput = spreadRadiusRow.inputElement;
+ this._spreadRadiusInput.type = "text"
+ this._spreadRadiusInput.addEventListener("input", this._handleSpreadRadiusInputInput.bind(this));
+ this._spreadRadiusInput.addEventListener("keydown", this._handleSpreadRadiusInputKeyDown.bind(this));
+
+ this._spreadRadiusSlider = createSlider(spreadRadiusRow.rowElement, -50, 50);
+ this._spreadRadiusSlider.addEventListener("input", this._handleSpreadRadiusSliderInput.bind(this));
+
+ this._colorPicker = new WI.ColorPicker;
+ this._colorPicker.addEventListener(WI.ColorPicker.Event.ColorChanged, this._handleColorChanged, this);
+ this._element.appendChild(this._colorPicker.element);
+
+ this.boxShadow = new WI.BoxShadow;
+
+ WI.addWindowKeydownListener(this);
+ }
+
+ // Public
+
+ get element() { return this._element; }
+
+ get boxShadow()
+ {
+ return this._boxShadow;
+ }
+
+ set boxShadow(boxShadow)
+ {
+ console.assert(boxShadow instanceof WI.BoxShadow);
+
+ this._boxShadow = boxShadow;
+
+ let offsetX = this._boxShadow?.offsetX || {value: 0, unit: ""};
+ let offsetY = this._boxShadow?.offsetY || {value: 0, unit: ""};
+ this._offsetXInput.value = offsetX.value + offsetX.unit;
+ this._offsetYInput.value = offsetY.value + offsetY.unit;
+
+ let offsetSliderCenter = this._offsetSliderAreaSize / 2;
+ let offsetSliderX = Number.constrain(offsetX.value + offsetSliderCenter, 0, this._offsetSliderAreaSize);
+ let offsetSliderY = Number.constrain(offsetY.value + offsetSliderCenter, 0, this._offsetSliderAreaSize);
+
+ this._offsetSliderLine.setAttribute("x2", offsetSliderX);
+ this._offsetSliderKnob.setAttribute("cx", offsetSliderX);
+
+ this._offsetSliderLine.setAttribute("y2", offsetSliderY);
+ this._offsetSliderKnob.setAttribute("cy", offsetSliderY);
+
+ let blurRadius = this._boxShadow?.blurRadius || {value: 0, unit: ""};
+ this._blurRadiusInput.value = blurRadius.value + blurRadius.unit;
+ this._blurRadiusSlider.value = blurRadius.value;
+
+ let spreadRadius = this._boxShadow?.spreadRadius || {value: 0, unit: ""};
+ this._spreadRadiusInput.value = spreadRadius.value + spreadRadius.unit;
+ this._spreadRadiusSlider.value = spreadRadius.value;
+
+ let inset = this._boxShadow?.inset || false;
+ this._insetCheckbox.checked = inset;
+
+ let color = this._boxShadow?.color || WI.Color.fromString("transparent");
+ this._colorPicker.color = color;
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ switch (event.type) {
+ case "keydown":
+ console.assert(event.target === this._offsetSliderSVG);
+ this._handleOffsetSliderSVGKeyDown(event);
+ return;
+
+ case "mousedown":
+ console.assert(event.target === this._offsetSliderSVG);
+ this._handleOffsetSliderSVGMouseDown(event);
+ return;
+
+ case "mousemove":
+ this._handleWindowMouseMove(event);
+ return;
+
+ case "mouseup":
+ this._handleWindowMouseUp(event);
+ return;
+ }
+
+ console.assert();
+ }
+
+ // Private
+
+ _updateBoxShadow({offsetX, offsetY, blurRadius, spreadRadius, inset, color})
+ {
+ let change = false;
+
+ if (!offsetX)
+ offsetX = this._boxShadow.offsetX;
+ else if (!Object.shallowEqual(offsetX, this._boxShadow.offsetX))
+ change = true;
+
+ if (!offsetY)
+ offsetY = this._boxShadow.offsetY;
+ else if (!Object.shallowEqual(offsetY, this._boxShadow.offsetY))
+ change = true;
+
+ if (!blurRadius)
+ blurRadius = this._boxShadow.blurRadius;
+ else if (!Object.shallowEqual(blurRadius, this._boxShadow.blurRadius))
+ change = true;
+
+ if (!spreadRadius)
+ spreadRadius = this._boxShadow.spreadRadius;
+ else if (!Object.shallowEqual(spreadRadius, this._boxShadow.spreadRadius))
+ change = true;
+
+ if (inset === undefined)
+ inset = this._boxShadow.inset;
+ else if (inset !== this._boxShadow.inset)
+ change = true;
+
+ if (!color)
+ color = this._boxShadow.color;
+ else if (color.toString() !== this._boxShadow.color?.toString())
+ change = true;
+
+ if (!change)
+ return;
+
+ this.boxShadow = new WI.BoxShadow(offsetX, offsetY, blurRadius, spreadRadius, inset, color);
+
+ this.dispatchEventToListeners(WI.BoxShadowEditor.Event.BoxShadowChanged, {boxShadow: this._boxShadow});
+ }
+
+ _updateBoxShadowOffsetFromSliderMouseEvent(event, saveMouseDownPoint)
+ {
+ let point = WI.Point.fromEventInElement(event, this._offsetSliderSVG);
+ point.x = Number.constrain(point.x - this._offsetSliderKnobRadius, 0, this._offsetSliderAreaSize);
+ point.y = Number.constrain(point.y - this._offsetSliderKnobRadius, 0, this._offsetSliderAreaSize);
+
+ if (saveMouseDownPoint)
+ this._offsetSliderSVGMouseDownPoint = point;
+
+ if (event.shiftKey && this._offsetSliderSVGMouseDownPoint) {
+ if (Math.abs(this._offsetSliderSVGMouseDownPoint.x - point.x) > Math.abs(this._offsetSliderSVGMouseDownPoint.y - point.y))
+ point.y = this._offsetSliderSVGMouseDownPoint.y;
+ else
+ point.x = this._offsetSliderSVGMouseDownPoint.x;
+ }
+
+ let offsetSliderCenter = this._offsetSliderAreaSize / 2;
+
+ this._updateBoxShadow({
+ offsetX: {
+ value: point.x - offsetSliderCenter,
+ unit: this._boxShadow.offsetX.unit,
+ },
+ offsetY: {
+ value: point.y - offsetSliderCenter,
+ unit: this._boxShadow.offsetY.unit,
+ },
+ });
+ }
+
+ _determineShiftForEvent(event)
+ {
+ let shift = 0;
+ if (event.key === "ArrowUp")
+ shift = 1;
+ else if (event.key === "ArrowDown")
+ shift = -1;
+
+ if (!shift)
+ return NaN;
+
+ if (event.metaKey)
+ shift *= 100;
+ else if (event.shiftKey)
+ shift *= 10;
+ else if (event.altKey)
+ shift /= 10;
+
+ event.preventDefault();
+
+ return shift;
+ }
+
+ _handleOffsetSliderSVGKeyDown(event) {
+ let shiftX = 0;
+ let shiftY = 0;
+
+ switch (event.keyCode) {
+ case WI.KeyboardShortcut.Key.Up.keyCode:
+ shiftY = -1;
+ break;
+
+ case WI.KeyboardShortcut.Key.Right.keyCode:
+ shiftX = 1;
+ break;
+
+ case WI.KeyboardShortcut.Key.Down.keyCode:
+ shiftY = 1;
+ break;
+
+ case WI.KeyboardShortcut.Key.Left.keyCode:
+ shiftX = -1;
+ break;
+ }
+
+ if (!shiftX && !shiftY)
+ return false;
+
+ let multiplier = 1;
+
+ if (event.shiftKey)
+ multiplier = 10;
+ else if (event.altKey)
+ multiplier = 0.1;
+
+ shiftX *= multiplier;
+ shiftY *= multiplier;
+
+ let offsetValueLimit = this._offsetSliderAreaSize / 2;
+
+ this._updateBoxShadow({
+ offsetX: {
+ value: Number.constrain(this._boxShadow.offsetX.value + shiftX, -1 * offsetValueLimit, offsetValueLimit).maxDecimals(1),
+ unit: this._boxShadow.offsetX.unit,
+ },
+ offsetY: {
+ value: Number.constrain(this._boxShadow.offsetY.value + shiftY, -1 * offsetValueLimit, offsetValueLimit).maxDecimals(1),
+ unit: this._boxShadow.offsetY.unit,
+ },
+ });
+
+ }
+
+ _handleOffsetSliderSVGMouseDown(event)
+ {
+ if (event.button !== 0)
+ return;
+
+ event.stop();
+
+ this._offsetSliderSVG.focus();
+
+ window.addEventListener("mousemove", this, true);
+ window.addEventListener("mouseup", this, true);
+
+ this._updateBoxShadowOffsetFromSliderMouseEvent(event, true);
+ }
+
+ _handleWindowMouseMove(event)
+ {
+ this._updateBoxShadowOffsetFromSliderMouseEvent(event);
+ }
+
+ _handleWindowMouseUp(event)
+ {
+ this._offsetSliderSVGMouseDownPoint = null;
+
+ window.removeEventListener("mousemove", this, true);
+ window.removeEventListener("mouseup", this, true);
+ }
+
+ _handleOffsetXInputInput(event)
+ {
+ this._updateBoxShadow({
+ offsetX: WI.BoxShadow.parseNumberComponent(this._offsetXInput.value),
+ });
+ }
+
+ _handleOffsetXInputKeyDown(event)
+ {
+ let shift = this._determineShiftForEvent(event);
+ if (isNaN(shift))
+ return;
+
+ this._updateBoxShadow({
+ offsetX: {
+ value: (this._boxShadow.offsetX.value + shift).maxDecimals(1),
+ unit: this._boxShadow.offsetX.unit,
+ },
+ });
+ }
+
+ _handleOffsetYInputInput(event)
+ {
+ this._updateBoxShadow({
+ offsetY: WI.BoxShadow.parseNumberComponent(this._offsetYInput.value),
+ });
+ }
+
+ _handleOffsetYInputKeyDown(event)
+ {
+ let shift = this._determineShiftForEvent(event);
+ if (isNaN(shift))
+ return;
+
+ this._updateBoxShadow({
+ offsetY: {
+ value: (this._boxShadow.offsetY.value + shift).maxDecimals(1),
+ unit: this._boxShadow.offsetY.unit,
+ },
+ });
+ }
+
+ _handleBlurRadiusInputInput(event)
+ {
+ this._updateBoxShadow({
+ blurRadius: WI.BoxShadow.parseNumberComponent(this._blurRadiusInput.value),
+ });
+ }
+
+ _handleBlurRadiusInputKeyDown(event)
+ {
+ let shift = this._determineShiftForEvent(event);
+ if (isNaN(shift))
+ return;
+
+ this._updateBoxShadow({
+ blurRadius: {
+ value: (this._boxShadow.blurRadius.value + shift).maxDecimals(1),
+ unit: this._boxShadow.blurRadius.unit,
+ }
+ });
+ }
+
+ _handleBlurRadiusSliderInput(event)
+ {
+ this._updateBoxShadow({
+ blurRadius: {
+ value: this._blurRadiusSlider.valueAsNumber,
+ unit: this._boxShadow.blurRadius.unit,
+ }
+ });
+ }
+
+ _handleSpreadRadiusInputInput(event)
+ {
+ this._updateBoxShadow({
+ spreadRadius: WI.BoxShadow.parseNumberComponent(this._spreadRadiusInput.value),
+ });
+ }
+
+ _handleSpreadRadiusInputKeyDown(event)
+ {
+ let shift = this._determineShiftForEvent(event);
+ if (isNaN(shift))
+ return;
+
+ this._updateBoxShadow({
+ spreadRadius: {
+ value: (this._boxShadow.spreadRadius.value + shift).maxDecimals(1),
+ unit: this._boxShadow.spreadRadius.unit,
+ }
+ });
+ }
+
+ _handleSpreadRadiusSliderInput(event)
+ {
+ this._updateBoxShadow({
+ spreadRadius: {
+ value: this._spreadRadiusSlider.valueAsNumber,
+ unit: this._boxShadow.spreadRadius.unit,
+ }
+ });
+ }
+
+ _handleInsetCheckboxChange(event)
+ {
+ this._updateBoxShadow({
+ inset: !!this._insetCheckbox.checked,
+ });
+ }
+
+ _handleColorChanged(event)
+ {
+ this._updateBoxShadow({
+ color: this._colorPicker.color,
+ });
+ }
+};
+
+WI.BoxShadowEditor.Event = {
+ BoxShadowChanged: "box-shadow-editor-box-shadow-changed"
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ColorPicker.css (259169 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Views/ColorPicker.css 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ColorPicker.css 2020-03-29 02:56:05 UTC (rev 259170)
@@ -29,7 +29,6 @@
height: 236px;
padding: 5px;
- --color-picker-width: 256px;
--color-picker-hue-offset-start: 41px;
--color-picker-opacity-offset-start: 18px;
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.css (259169 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.css 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.css 2020-03-29 02:56:05 UTC (rev 259170)
@@ -31,12 +31,16 @@
height: 1em;
margin-right: 3px;
vertical-align: -2px;
- background-color: var(--background-color);
border-radius: 2px;
overflow: hidden;
cursor: default;
}
+.inline-swatch:not(.box-shadow),
+.inline-swatch.box-shadow:matches(:hover, :active) {
+ background-color: var(--background-color-content);
+}
+
.inline-swatch:matches(.color, .gradient) {
/* Make a checkered background for transparent colors to show against. */
background-image: linear-gradient(to bottom, hsl(0, 0%, 80%), hsl(0, 0%, 80%)),
@@ -50,15 +54,15 @@
color: var(--glyph-color-active);
}
-.inline-swatch:matches(.bezier, .spring, .variable) {
+.inline-swatch:matches(.bezier, .box-shadow, .spring, .variable) {
margin-right: 2px;
}
-.inline-swatch:not(.read-only):matches(.bezier, .spring, .variable):hover {
+.inline-swatch:not(.read-only):matches(.bezier, .box-shadow, .spring, .variable):hover {
filter: brightness(0.9);
}
-.inline-swatch:not(.read-only):matches(.bezier, .spring, .variable):active {
+.inline-swatch:not(.read-only):matches(.bezier, .box-shadow, .spring, .variable):active {
filter: brightness(0.8);
}
@@ -90,7 +94,7 @@
border-color: hsl(0, 0%, 25%);
}
-.inline-swatch:matches(.bezier, .spring, .variable) > span {
+.inline-swatch:matches(.bezier, .box-shadow, .spring, .variable) > span {
display: none;
}
@@ -114,3 +118,9 @@
.inline-swatch-variable-popover .CodeMirror pre {
padding: 0 3px;
}
+
+@media (prefers-color-scheme: dark) {
+ .inline-swatch.box-shadow > svg {
+ filter: invert();
+ }
+}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js (259169 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js 2020-03-29 02:56:05 UTC (rev 259170)
@@ -32,37 +32,59 @@
this._type = type;
- if (this._type === WI.InlineSwatch.Type.Bezier || this._type === WI.InlineSwatch.Type.Spring)
+ switch (this._type) {
+ case WI.InlineSwatch.Type.Bezier:
+ case WI.InlineSwatch.Type.Spring:
this._swatchElement = WI.ImageUtilities.useSVGSymbol("Images/CubicBezier.svg");
- else if (this._type === WI.InlineSwatch.Type.Variable)
+ break;
+
+ case WI.InlineSwatch.Type.BoxShadow:
+ this._swatchElement = WI.ImageUtilities.useSVGSymbol("Images/BoxShadow.svg");
+ break;
+
+ case WI.InlineSwatch.Type.Variable:
this._swatchElement = WI.ImageUtilities.useSVGSymbol("Images/CSSVariable.svg");
- else
+ break;
+
+ default:
this._swatchElement = document.createElement("span");
+ break;
+ }
- this._swatchElement.classList.add("inline-swatch", this._type.split("-").lastValue);
+ this._swatchElement.classList.add("inline-swatch", this._type.replace("inline-swatch-type-", ""));
if (readOnly)
this._swatchElement.classList.add("read-only");
else {
switch (this._type) {
+ case WI.InlineSwatch.Type.Bezier:
+ this._swatchElement.title = WI.UIString("Edit \u201Ccubic-bezier\u201D function");
+ break;
+
+ case WI.InlineSwatch.Type.BoxShadow:
+ this._swatchElement.title = WI.UIString("Edit \u201Cbox-shadow\u201D");
+ break;
+
case WI.InlineSwatch.Type.Color:
// Handled later by _updateSwatch.
break;
+
case WI.InlineSwatch.Type.Gradient:
this._swatchElement.title = WI.UIString("Edit custom gradient");
break;
- case WI.InlineSwatch.Type.Bezier:
- this._swatchElement.title = WI.UIString("Edit \u201Ccubic-bezier\u201D function");
+
+ case WI.InlineSwatch.Type.Image:
+ this._swatchElement.title = WI.UIString("View Image");
break;
+
case WI.InlineSwatch.Type.Spring:
this._swatchElement.title = WI.UIString("Edit \u201Cspring\u201D function");
break;
+
case WI.InlineSwatch.Type.Variable:
this._swatchElement.title = WI.UIString("Click to view variable value\nShift-click to replace variable with value");
break;
- case WI.InlineSwatch.Type.Image:
- this._swatchElement.title = WI.UIString("View Image");
- break;
+
default:
WI.reportInternalError(`Unknown InlineSwatch type "${type}"`);
break;
@@ -127,12 +149,14 @@
switch (this._type) {
case WI.InlineSwatch.Type.Bezier:
return WI.CubicBezier.fromString("linear");
+ case WI.InlineSwatch.Type.BoxShadow:
+ return WI.BoxShadow.fromString("none");
+ case WI.InlineSwatch.Type.Color:
+ return WI.Color.fromString("white");
case WI.InlineSwatch.Type.Gradient:
return WI.Gradient.fromString("linear-gradient(transparent, transparent)");
case WI.InlineSwatch.Type.Spring:
return WI.Spring.fromString("1 100 10 0");
- case WI.InlineSwatch.Type.Color:
- return WI.Color.fromString("white");
default:
return null;
}
@@ -209,6 +233,16 @@
this._valueEditor = null;
switch (this._type) {
+ case WI.InlineSwatch.Type.Bezier:
+ this._valueEditor = new WI.BezierEditor;
+ this._valueEditor.addEventListener(WI.BezierEditor.Event.BezierChanged, this._valueEditorValueDidChange, this);
+ break;
+
+ case WI.InlineSwatch.Type.BoxShadow:
+ this._valueEditor = new WI.BoxShadowEditor;
+ this._valueEditor.addEventListener(WI.BoxShadowEditor.Event.BoxShadowChanged, this._valueEditorValueDidChange, this);
+ break;
+
case WI.InlineSwatch.Type.Color:
this._valueEditor = new WI.ColorPicker;
this._valueEditor.addEventListener(WI.ColorPicker.Event.ColorChanged, this._valueEditorValueDidChange, this);
@@ -220,9 +254,15 @@
this._valueEditor.addEventListener(WI.GradientEditor.Event.ColorPickerToggled, (event) => popover.update());
break;
- case WI.InlineSwatch.Type.Bezier:
- this._valueEditor = new WI.BezierEditor;
- this._valueEditor.addEventListener(WI.BezierEditor.Event.BezierChanged, this._valueEditorValueDidChange, this);
+ case WI.InlineSwatch.Type.Image:
+ if (value.src) {
+ this._valueEditor = {};
+ this._valueEditor.element = document.createElement("img");
+ this._valueEditor.element.src = ""
+ this._valueEditor.element.classList.add("show-grid");
+ this._valueEditor.element.style.setProperty("max-width", "50vw");
+ this._valueEditor.element.style.setProperty("max-height", "50vh");
+ }
break;
case WI.InlineSwatch.Type.Spring:
@@ -244,17 +284,6 @@
popover.update();
});
break;
-
- case WI.InlineSwatch.Type.Image:
- if (value.src) {
- this._valueEditor = {};
- this._valueEditor.element = document.createElement("img");
- this._valueEditor.element.src = ""
- this._valueEditor.element.classList.add("show-grid");
- this._valueEditor.element.style.setProperty("max-width", "50vw");
- this._valueEditor.element.style.setProperty("max-height", "50vh");
- }
- break;
}
if (!this._valueEditor)
@@ -266,6 +295,14 @@
this.dispatchEventToListeners(WI.InlineSwatch.Event.Activated);
switch (this._type) {
+ case WI.InlineSwatch.Type.Bezier:
+ this._valueEditor.bezier = value;
+ break;
+
+ case WI.InlineSwatch.Type.BoxShadow:
+ this._valueEditor.boxShadow = value;
+ break;
+
case WI.InlineSwatch.Type.Color:
this._valueEditor.color = value;
this._valueEditor.focus();
@@ -275,10 +312,6 @@
this._valueEditor.gradient = value;
break;
- case WI.InlineSwatch.Type.Bezier:
- this._valueEditor.bezier = value;
- break;
-
case WI.InlineSwatch.Type.Spring:
this._valueEditor.spring = value;
break;
@@ -308,14 +341,27 @@
_valueEditorValueDidChange(event)
{
- if (this._type === WI.InlineSwatch.Type.Color)
+ switch (this._type) {
+ case WI.InlineSwatch.Type.BoxShadow:
+ this._value = event.data.boxShadow;
+ break;
+
+ case WI.InlineSwatch.Type.Bezier:
+ this._value = event.data.bezier;
+ break;
+
+ case WI.InlineSwatch.Type.Color:
this._value = event.data.color;
- else if (this._type === WI.InlineSwatch.Type.Gradient)
+ break;
+
+ case WI.InlineSwatch.Type.Gradient:
this._value = event.data.gradient;
- else if (this._type === WI.InlineSwatch.Type.Bezier)
- this._value = event.data.bezier;
- else if (this._type === WI.InlineSwatch.Type.Spring)
+ break;
+
+ case WI.InlineSwatch.Type.Spring:
this._value = event.data.spring;
+ break;
+ }
this._updateSwatch();
}
@@ -454,6 +500,7 @@
Color: "inline-swatch-type-color",
Gradient: "inline-swatch-type-gradient",
Bezier: "inline-swatch-type-bezier",
+ BoxShadow: "inline-swatch-type-box-shadow",
Spring: "inline-swatch-type-spring",
Variable: "inline-swatch-type-variable",
Image: "inline-swatch-type-image",
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js (259169 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js 2020-03-29 02:56:05 UTC (rev 259170)
@@ -552,6 +552,9 @@
{
// FIXME: <https://webkit.org/b/178636> Web Inspector: Styles: Make inline widgets work with CSS functions (var(), calc(), etc.)
+ if (this._property.name === "box-shadow")
+ return this._addBoxShadowTokens(tokens);
+
tokens = this._addVariableTokens(tokens);
if (this._property.isVariable || WI.CSSKeywordCompletions.isColorAwareProperty(this._property.name)) {
@@ -589,7 +592,7 @@
}
let rawTokens = tokens.slice(gradientStartIndex, i + 1);
- let text = rawTokens.map((token) => token.value).join("");
+ let text = this._resolveVariables(rawTokens.map((token) => token.value).join(""));
let gradient = WI.Gradient.fromString(text);
if (gradient)
newTokens.push(this._createInlineSwatch(WI.InlineSwatch.Type.Gradient, rawTokens, gradient));
@@ -607,6 +610,7 @@
_addColorTokens(tokens)
{
let newTokens = [];
+ let openParentheses = 0;
let pushPossibleColorToken = (text, ...rawTokens) => {
let color = WI.Color.fromString(text);
@@ -630,12 +634,21 @@
// Color keyword
pushPossibleColorToken(token.value, token);
} else if (!isNaN(colorFunctionStartIndex)) {
- // Color Function end
- if (token.value !== ")")
+ if (token.value === "(") {
+ ++openParentheses;
continue;
+ }
+ if (token.value === ")") {
+ --openParentheses;
+ if (openParentheses)
+ continue;
+ }
+
+ // Color Function end
+
let rawTokens = tokens.slice(colorFunctionStartIndex, i + 1);
- let text = rawTokens.map((token) => token.value).join("");
+ let text = this._resolveVariables(rawTokens.map((token) => token.value).join(""));
pushPossibleColorToken(text, ...rawTokens);
colorFunctionStartIndex = NaN;
} else
@@ -665,7 +678,7 @@
continue;
let rawTokens = tokens.slice(startIndex, i + 1);
- let text = rawTokens.map((token) => token.value).join("");
+ let text = this._resolveVariables(rawTokens.map((token) => token.value).join(""));
let valueObject;
let inlineSwatchType;
@@ -692,6 +705,61 @@
return newTokens;
}
+ _addBoxShadowTokens(tokens)
+ {
+ let newTokens = [];
+ let startIndex = 0;
+ let openParentheses = 0;
+
+ for (let i = 0; i <= tokens.length; i++) {
+ let token = tokens[i];
+ if (i === tokens.length || (token.value === "," && !openParentheses)) {
+ let rawTokens = tokens.slice(startIndex, i);
+
+ let firstNonWhitespaceIndex = Infinity;
+ let lastNonWhitespaceIndex = 0;
+ for (let j = 0; j < rawTokens.length; ++j) {
+ if (/\s/.test(rawTokens[j].value))
+ continue;
+
+ firstNonWhitespaceIndex = Math.min(firstNonWhitespaceIndex, j);
+ lastNonWhitespaceIndex = Math.max(lastNonWhitespaceIndex, j);
+ }
+
+ let nonWhitespaceTokens = rawTokens.slice(firstNonWhitespaceIndex, lastNonWhitespaceIndex + 1);
+
+ newTokens.pushAll(rawTokens.slice(0, firstNonWhitespaceIndex));
+
+ let text = this._resolveVariables(nonWhitespaceTokens.map((rawToken) => rawToken.value).join(""));
+ let boxShadow = WI.BoxShadow.fromString(text);
+ if (boxShadow)
+ newTokens.push(this._createInlineSwatch(WI.InlineSwatch.Type.BoxShadow, nonWhitespaceTokens, boxShadow));
+ else
+ newTokens.pushAll(nonWhitespaceTokens);
+
+ newTokens.pushAll(rawTokens.slice(lastNonWhitespaceIndex + 1));
+
+ if (token)
+ newTokens.push(token);
+
+ startIndex = i + 1;
+ continue;
+ }
+
+ if (token.value === "(") {
+ ++openParentheses;
+ continue;
+ }
+
+ if (token.value === ")") {
+ --openParentheses;
+ continue;
+ }
+ }
+
+ return newTokens;
+ }
+
_addVariableTokens(tokens)
{
let newTokens = [];
@@ -742,6 +810,11 @@
return newTokens;
}
+ _resolveVariables(cssText)
+ {
+ return cssText.replace(/var\(--[^\)]+\)/g, (match) => this._property.ownerStyle.nodeStyles.computedStyle.resolveVariableValue(match) || match);
+ }
+
_handleNameChange()
{
this._property.name = this._nameElement.textContent.trim();
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css (259169 => 259170)
--- trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css 2020-03-29 02:54:09 UTC (rev 259169)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css 2020-03-29 02:56:05 UTC (rev 259170)
@@ -196,6 +196,8 @@
--diff-deletion-text-color: hsl(0, 100%, 35%);
--diff-deletion-background-color: hsl(5, 100%, 94%);
--diff-addition-border-color: hsl(90, 100%, 40%);
+
+ --color-picker-width: 256px;
}
body.window-inactive {