Diff
Modified: trunk/Source/WebInspectorUI/ChangeLog (237658 => 237659)
--- trunk/Source/WebInspectorUI/ChangeLog 2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/ChangeLog 2018-10-31 22:52:11 UTC (rev 237659)
@@ -1,3 +1,70 @@
+2018-10-31 Nikita Vasilyev <nvasil...@apple.com>
+
+ Web Inspector: Styles: implement copying and deletion of multiple properties
+ https://bugs.webkit.org/show_bug.cgi?id=191037
+ <rdar://problem/45650078>
+
+ Reviewed by Brian Burg.
+
+ This patch should only work with "Enable Selection of Multiple Properties" checked. It shouldn't introduce any
+ changes when this setting is unchecked.
+
+ Mousedown on a property (1) and moving the mouse cursor to another property (2) should select properties 1, 2, and
+ all properties between them until mouseup is fired.
+
+ Once selected:
+ - Pressing Command-C should copy the selected properties.
+ - Pressing Delete should remove the properties.
+
+ * UserInterface/Models/CSSProperty.js:
+ (WI.CSSProperty.prototype.get formattedText):
+ * UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css:
+ (.spreadsheet-style-declaration-editor):
+ (.multiple-properties-selection .spreadsheet-style-declaration-editor .property):
+ (.multiple-properties-selection .spreadsheet-style-declaration-editor :matches(.name, .value):not(.editing)):
+ (.multiple-properties-selection .spreadsheet-style-declaration-editor .property.selected):
+ (.multiple-properties-selection .spreadsheet-style-declaration-editor .property.selected:focus):
+ (@media (prefers-dark-interface)):
+
+ * UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js:
+ (WI.SpreadsheetCSSStyleDeclarationEditor):
+ (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.initialLayout):
+ (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.selectProperties):
+ (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.deselectProperties):
+ (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyBlur):
+ (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyMouseEnter):
+ (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyMouseLeave):
+ (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyCopy):
+ (WI.SpreadsheetCSSStyleDeclarationEditor.prototype._handleKeyDown):
+ (WI.SpreadsheetCSSStyleDeclarationEditor.prototype._hasSelectedProperties):
+ Property selection is defined as two numbers: anchorIndex and focusIndex.
+ The property with focusIndex is actually focused. The focus outline is replaced by a more subtle left blue border.
+
+ * UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.css:
+ (.spreadsheet-css-declaration.selecting,):
+ * UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js:
+ (WI.SpreadsheetCSSStyleDeclarationSection):
+ (WI.SpreadsheetCSSStyleDeclarationSection.prototype.spreadsheetCSSStyleDeclarationEditorPropertyBlur):
+ (WI.SpreadsheetCSSStyleDeclarationSection.prototype.spreadsheetCSSStyleDeclarationEditorPropertyMouseEnter):
+ (WI.SpreadsheetCSSStyleDeclarationSection.prototype.spreadsheetCSSStyleDeclarationEditorPropertyMouseLeave):
+ (WI.SpreadsheetCSSStyleDeclarationSection.prototype._handleMouseDown):
+ (WI.SpreadsheetCSSStyleDeclarationSection.prototype._handleWindowMouseUp):
+ (WI.SpreadsheetCSSStyleDeclarationSection.prototype._handleClick):
+ * UserInterface/Views/SpreadsheetStyleProperty.js:
+ (WI.SpreadsheetStyleProperty):
+ Implement copying the same way it's done for DataGrid: by adding copyHandler property to the focused element.
+ Add `tabIndex=-1` so the property element can be focused.
+
+ (WI.SpreadsheetStyleProperty.prototype.get property):
+ (WI.SpreadsheetStyleProperty.prototype.get selected):
+ (WI.SpreadsheetStyleProperty.prototype.set selected):
+ (WI.SpreadsheetStyleProperty.prototype.remove):
+ (WI.SpreadsheetStyleProperty.prototype.updateStatus):
+ (WI.SpreadsheetStyleProperty.prototype.handleCopyEvent):
+ (WI.SpreadsheetStyleProperty.prototype.spreadsheetTextFieldDidCommit):
+ (WI.SpreadsheetStyleProperty.prototype.spreadsheetTextFieldDidBlur):
+ (WI.SpreadsheetStyleProperty.prototype._handleNamePaste):
+
2018-10-31 Devin Rousso <drou...@apple.com>
Web Inspector: Audit: attempt to re-link DOM nodes for imported results
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js (237658 => 237659)
--- trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js 2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js 2018-10-31 22:52:11 UTC (rev 237659)
@@ -164,6 +164,14 @@
this._text = newText;
}
+ get formattedText()
+ {
+ if (!this._name)
+ return "";
+
+ return `${this._name}: ${this._rawValue};`;
+ }
+
get name()
{
return this._name;
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css (237658 => 237659)
--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css 2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css 2018-10-31 22:52:11 UTC (rev 237659)
@@ -30,6 +30,9 @@
font-size: 11px;
color: hsl(0, 0%, 70%);
-webkit-user-select: text;
+
+ --background-color-selected: hsl(210, 98%, 93%);
+ --border-color-selected: hsl(225, 91%, 70%);
}
.spreadsheet-style-declaration-editor .property {
@@ -37,6 +40,11 @@
padding-left: calc(var(--css-declaration-horizontal-padding) + 17px);
}
+.multiple-properties-selection .spreadsheet-style-declaration-editor .property {
+ border-left: 1px solid transparent;
+ outline: none;
+}
+
.spreadsheet-style-declaration-editor .property:not(.disabled) .name {
color: var(--syntax-highlight-boolean-color);
}
@@ -45,6 +53,10 @@
color: var(--text-color);
}
+.multiple-properties-selection .spreadsheet-style-declaration-editor :matches(.name, .value):not(.editing) {
+ outline: none;
+}
+
.spreadsheet-style-declaration-editor :matches(.name, .value).editing {
outline: 1px solid white !important;
box-shadow: 0 1px 2px 1px hsla(0, 0%, 0%, 0.6);
@@ -124,6 +136,14 @@
-webkit-clip-path: polygon(0% 50%, 6px 0%, 100% 0%, 100% 100%, 6px 100%);
}
+.multiple-properties-selection .spreadsheet-style-declaration-editor .property.selected {
+ background-color: var(--background-color-selected);
+}
+
+.multiple-properties-selection .spreadsheet-style-declaration-editor .property.selected:focus {
+ border-left-color: var(--border-color-selected);
+}
+
.spreadsheet-style-declaration-editor .property:matches(.implicit, .not-inherited) .content > * {
opacity: 0.5;
}
@@ -160,6 +180,11 @@
}
@media (prefers-dark-interface) {
+ .spreadsheet-style-declaration-editor {
+ --background-color-selected: hsl(230, 51%, 36%);
+ --border-color-selected: hsl(216, 98%, 67%);
+ }
+
.spreadsheet-style-declaration-editor :matches(.name, .value).editing {
outline-color: var(--background-color-secondary) !important;
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js (237658 => 237659)
--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js 2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js 2018-10-31 22:52:11 UTC (rev 237659)
@@ -47,6 +47,9 @@
this._propertyPendingStartEditing = null;
this._pendingAddBlankPropertyIndexOffset = NaN;
this._filterText = null;
+
+ this._anchorIndex = NaN;
+ this._focusIndex = NaN;
}
// Public
@@ -64,6 +67,9 @@
this.focused = false;
}, true);
+
+ if (WI.settings.experimentalEnableMultiplePropertiesSelection.value)
+ this.element.addEventListener("keydown", this._handleKeyDown.bind(this));
}
layout()
@@ -307,6 +313,43 @@
this.needsLayout();
}
+ selectProperties(anchorIndex, focusIndex)
+ {
+ console.assert(anchorIndex < this._propertyViews.length, `anchorIndex (${anchorIndex}) is greater than the last property index (${this._propertyViews.length})`);
+ console.assert(focusIndex < this._propertyViews.length, `focusIndex (${focusIndex}) is greater than the last property index (${this._propertyViews.length})`);
+
+ if (isNaN(anchorIndex) || isNaN(focusIndex)) {
+ console.error(`Nothing to select. anchorIndex (${anchorIndex}) and focusIndex (${focusIndex}) must be numbers.`);
+ this.deselectProperties();
+ return;
+ }
+
+ this._anchorIndex = anchorIndex;
+ this._focusIndex = focusIndex;
+
+ let startIndex = Math.min(anchorIndex, focusIndex);
+ let endIndex = Math.max(anchorIndex, focusIndex);
+
+ for (let i = 0; i < this._propertyViews.length; ++i) {
+ let propertyView = this._propertyViews[i];
+ let isSelected = i >= startIndex && i <= endIndex;
+ propertyView.selected = isSelected;
+ }
+
+ let property = this._propertyViews[focusIndex];
+ property.element.focus();
+ }
+
+ deselectProperties()
+ {
+ for (let propertyView of this._propertyViews)
+ propertyView.selected = false;
+
+ this._focused = false;
+ this._anchorIndex = NaN;
+ this._focusIndex = NaN;
+ }
+
applyFilter(filterText)
{
this._filterText = filterText;
@@ -330,6 +373,24 @@
// SpreadsheetStyleProperty delegate
+ spreadsheetStylePropertyBlur(event, property)
+ {
+ if (this._suppressBlur)
+ return;
+
+ this._delegate.spreadsheetCSSStyleDeclarationEditorPropertyBlur(event, property);
+ }
+
+ spreadsheetStylePropertyMouseEnter(event, property)
+ {
+ this._delegate.spreadsheetCSSStyleDeclarationEditorPropertyMouseEnter(event, property);
+ }
+
+ spreadsheetStylePropertyMouseLeave(event, property)
+ {
+ this._delegate.spreadsheetCSSStyleDeclarationEditorPropertyMouseLeave(event, property);
+ }
+
spreadsheetStylePropertyFocusMoved(propertyView, {direction, willRemoveProperty})
{
this._updatePropertiesStatus();
@@ -384,6 +445,23 @@
this._pendingAddBlankPropertyIndexOffset = this._propertyViews.length - index;
}
+ spreadsheetStylePropertyCopy(event)
+ {
+ if (!this._hasSelectedProperties())
+ return;
+
+ let formattedProperties = [];
+ let startIndex = Math.min(this._anchorIndex, this._focusIndex);
+ let endIndex = Math.max(this._anchorIndex, this._focusIndex);
+ for (let i = startIndex; i <= endIndex; ++i) {
+ let propertyView = this._propertyViews[i];
+ formattedProperties.push(propertyView.property.formattedText);
+ }
+
+ event.clipboardData.setData("text/plain", formattedProperties.join("\n"));
+ event.stop();
+ }
+
spreadsheetStylePropertyRemoved(propertyView)
{
this._propertyViews.remove(propertyView);
@@ -412,6 +490,53 @@
// Private
+ _handleKeyDown(event)
+ {
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
+ let delta = event.key === "ArrowUp" ? -1 : 1;
+ let focusIndex = Number.constrain(this._focusIndex + delta, 0, this._propertyViews.length - 1);
+
+ // Blur event deselects all properties.
+ this._suppressBlur = true;
+ this.selectProperties(focusIndex, focusIndex);
+ this._suppressBlur = false;
+
+ event.stop();
+ } else if (event.key === "Backspace") {
+ if (!this._hasSelectedProperties())
+ return;
+
+ let startIndex = Math.min(this._anchorIndex, this._focusIndex);
+ let endIndex = Math.max(this._anchorIndex, this._focusIndex);
+
+ let propertyIndexToSelect = NaN;
+ if (endIndex + 1 !== this._propertyViews.length)
+ propertyIndexToSelect = startIndex;
+ else if (startIndex > 0)
+ propertyIndexToSelect = startIndex - 1;
+
+ this.deselectProperties();
+
+ for (let i = endIndex; i >= startIndex; i--)
+ this._propertyViews[i].remove();
+
+ if (!isNaN(propertyIndexToSelect)) {
+ this._suppressBlur = true;
+ this.selectProperties(propertyIndexToSelect, propertyIndexToSelect);
+ this._suppressBlur = false;
+ }
+
+ event.stop();
+
+ } else if (event.key === "Esc")
+ this.deselectProperties();
+ }
+
+ _hasSelectedProperties()
+ {
+ return !isNaN(this._anchorIndex) && !isNaN(this._focusIndex);
+ }
+
_editablePropertyAfter(propertyIndex)
{
for (let index = propertyIndex + 1; index < this._propertyViews.length; index++) {
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.css (237658 => 237659)
--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.css 2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.css 2018-10-31 22:52:11 UTC (rev 237659)
@@ -34,6 +34,11 @@
-webkit-user-select: text;
}
+.spreadsheet-css-declaration.selecting,
+.spreadsheet-css-declaration.selecting .spreadsheet-style-declaration-editor {
+ -webkit-user-select: none;
+}
+
.spreadsheet-css-declaration :matches(.header, .header-media) {
padding: 0 var(--css-declaration-horizontal-padding);
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js (237658 => 237659)
--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js 2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js 2018-10-31 22:52:11 UTC (rev 237659)
@@ -37,6 +37,9 @@
super(element);
+ if (WI.settings.experimentalEnableMultiplePropertiesSelection.value)
+ element.classList.add("multiple-properties-selection");
+
this._delegate = delegate || null;
this._style = style;
this._propertiesEditor = null;
@@ -45,6 +48,10 @@
this._filterText = null;
this._shouldFocusSelectorElement = false;
this._wasEditing = false;
+
+ this._isMousePressed = true;
+ this._mouseDownIndex = NaN;
+ this._startedSelection = false;
}
// Public
@@ -208,6 +215,26 @@
this._delegate.spreadsheetCSSStyleDeclarationSectionStartEditingAdjacentRule(this, delta);
}
+ spreadsheetCSSStyleDeclarationEditorPropertyBlur(event, property)
+ {
+ if (!this._startedSelection)
+ this._propertiesEditor.deselectProperties();
+ }
+
+ spreadsheetCSSStyleDeclarationEditorPropertyMouseEnter(event, property)
+ {
+ if (this._isMousePressed && this._startedSelection) {
+ let index = parseInt(property.element.dataset.propertyIndex);
+ this._propertiesEditor.selectProperties(this._mouseDownIndex, index);
+ }
+ }
+
+ spreadsheetCSSStyleDeclarationEditorPropertyMouseLeave(event, property)
+ {
+ if (this._isMousePressed)
+ this._startedSelection = true;
+ }
+
applyFilter(filterText)
{
this._filterText = filterText;
@@ -435,11 +462,50 @@
_handleMouseDown(event)
{
this._wasEditing = this._propertiesEditor.editing || document.activeElement === this._selectorElement;
+
+ if (!WI.settings.experimentalEnableMultiplePropertiesSelection.value)
+ return;
+
+ let propertyElement = event.target.closest(".property");
+ if (!propertyElement)
+ return;
+
+ this._isMousePressed = true;
+ this._startedSelection = false;
+
+ // Disable text selection on mousemove.
+ event.preventDefault();
+
+ // Canceling mousedown event prevents blur event from firing on the previously focused element.
+ if (this._wasEditing && document.activeElement)
+ document.activeElement.blur();
+
+ window.addEventListener("mouseup", this._handleWindowMouseUp.bind(this), {capture: true, once: true});
+
+ let propertyIndex = parseInt(propertyElement.dataset.propertyIndex);
+ this._propertiesEditor.deselectProperties();
+ this._mouseDownIndex = propertyIndex;
+
+ this._element.classList.add("selecting");
}
+ _handleWindowMouseUp(event)
+ {
+ if (this._startedSelection) {
+ // Don't start editing name/value if there's selection.
+ event.stop();
+ this._startedSelection = false;
+ }
+
+ this._isMousePressed = false;
+ this._mouseDownIndex = NaN;
+
+ this._element.classList.remove("selecting");
+ }
+
_handleClick(event)
{
- if (this._wasEditing)
+ if (this._wasEditing || this._startedSelection)
return;
if (window.getSelection().type === "Range")
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js (237658 => 237659)
--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js 2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js 2018-10-31 22:52:11 UTC (rev 237659)
@@ -44,16 +44,36 @@
this._property.__propertyView = this;
+ this._selected = false;
this._hasInvalidVariableValue = false;
this._update();
property.addEventListener(WI.CSSProperty.Event.OverriddenStatusChanged, this.updateStatus, this);
property.addEventListener(WI.CSSProperty.Event.Changed, this.updateStatus, this);
+
+ if (WI.settings.experimentalEnableMultiplePropertiesSelection.value && this._property.editable) {
+ this._element.tabIndex = -1;
+
+ this._element.addEventListener("blur", (event) => {
+ this._delegate.spreadsheetStylePropertyBlur(event, this);
+ });
+
+ this._element.addEventListener("mouseenter", (event) => {
+ this._delegate.spreadsheetStylePropertyMouseEnter(event, this);
+ });
+
+ this._element.addEventListener("mouseleave", (event) => {
+ this._delegate.spreadsheetStylePropertyMouseLeave(event, this);
+ });
+
+ this._element.copyHandler = this;
+ }
}
// Public
get element() { return this._element; }
+ get property() { return this._property; }
get nameTextField() { return this._nameTextField; }
get valueTextField() { return this._valueTextField; }
get enabled() { return this._property.enabled; }
@@ -63,6 +83,20 @@
this._element.dataset.propertyIndex = index;
}
+ get selected()
+ {
+ return this._selected;
+ }
+
+ set selected(value)
+ {
+ if (value === this._selected)
+ return;
+
+ this._selected = value;
+ this.updateStatus();
+ }
+
detached()
{
this._property.__propertyView = null;
@@ -87,6 +121,21 @@
this._element.classList.add("highlighted");
}
+ remove(replacement = null)
+ {
+ this.element.remove();
+
+ if (replacement)
+ this._property.replaceWithText(replacement);
+ else
+ this._property.remove();
+
+ this.detached();
+
+ if (this._delegate && typeof this._delegate.spreadsheetStylePropertyRemoved === "function")
+ this._delegate.spreadsheetStylePropertyRemoved(this);
+ }
+
updateStatus()
{
let duplicatePropertyExistsBelow = (cssProperty) => {
@@ -140,6 +189,9 @@
if (!this._property.enabled)
classNames.push("disabled");
+ if (this._selected)
+ classNames.push("selected");
+
this._element.className = classNames.join(" ");
this._element.title = elementTitle;
}
@@ -157,23 +209,13 @@
return matches;
}
- // Private
-
- _remove(replacement = "")
+ handleCopyEvent(event)
{
- this.element.remove();
+ this._delegate.spreadsheetStylePropertyCopy(event, this);
+ }
- if (replacement)
- this._property.replaceWithText(replacement);
- else
- this._property.remove();
+ // Private
- this.detached();
-
- if (this._delegate && typeof this._delegate.spreadsheetStylePropertyRemoved === "function")
- this._delegate.spreadsheetStylePropertyRemoved(this);
- }
-
_update()
{
this.element.removeChildren();
@@ -315,7 +357,7 @@
}
if (willRemoveProperty)
- this._remove();
+ this.remove();
}
spreadsheetTextFieldDidBlur(textField, event)
@@ -322,7 +364,7 @@
{
let focusedOutsideThisProperty = event.relatedTarget !== this._nameElement && event.relatedTarget !== this._valueElement;
if (focusedOutsideThisProperty && (!this._nameTextField.value.trim() || !this._valueTextField.value.trim())) {
- this._remove();
+ this.remove();
return;
}
@@ -617,7 +659,7 @@
event.preventDefault();
- this._remove(text);
+ this.remove(text);
if (this._delegate.spreadsheetStylePropertyAddBlankPropertySoon) {
this._delegate.spreadsheetStylePropertyAddBlankPropertySoon(this, {