aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-12-08 20:31:02 -0500
committerGitHub <noreply@github.com>2020-12-08 20:31:02 -0500
commit125c296eedf680ad7670544aa8f74d81fa9aa799 (patch)
treef91c4243fd08cad8258ec136228264d332569ab3
parent2be81cbb77f8a15f9049048078d69d5fee871f33 (diff)
Support frame resize on firefox (#1088)
* Add popup functions for getting/setting the frame size * Add frontend functions for getting/setting popup frame size * Expose display mode attribute on display HTML * Disable resizer on iframe * Add custom frame resizer handle * Add support for custom frame resizer
-rw-r--r--ext/fg/float.html7
-rw-r--r--ext/fg/js/frontend.js12
-rw-r--r--ext/fg/js/popup-factory.js14
-rw-r--r--ext/fg/js/popup-proxy.js8
-rw-r--r--ext/fg/js/popup-window.js8
-rw-r--r--ext/fg/js/popup.js19
-rw-r--r--ext/mixed/css/display.css50
-rw-r--r--ext/mixed/css/popup-outer.css3
-rw-r--r--ext/mixed/js/display.js74
9 files changed, 189 insertions, 6 deletions
diff --git a/ext/fg/float.html b/ext/fg/float.html
index 5874f44d..50c3b691 100644
--- a/ext/fg/float.html
+++ b/ext/fg/float.html
@@ -67,6 +67,13 @@
</div>
</div>
+<div class="frame-resizer-container">
+ <div class="frame-resizer-sizer1"><div class="frame-resizer-sizer2 frame-resizer-sizer2-with-min-size scrollbar"></div></div>
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1" preserveAspectRatio="none" class="frame-resizer-svg">
+ <path d="M1 0L1 1L0 1Z" class="frame-resizer-handle" id="frame-resizer-handle"/>
+ </svg>
+</div>
+
<!-- Scripts -->
<script src="/mixed/js/core.js"></script>
<script src="/mixed/js/yomichan.js"></script>
diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js
index 49c8a91c..cb105341 100644
--- a/ext/fg/js/frontend.js
+++ b/ext/fg/js/frontend.js
@@ -119,7 +119,9 @@ class Frontend {
['copySelection', {async: false, handler: this._onApiCopySelection.bind(this)}],
['getSelectionText', {async: false, handler: this._onApiGetSelectionText.bind(this)}],
['getPopupInfo', {async: false, handler: this._onApiGetPopupInfo.bind(this)}],
- ['getDocumentInformation', {async: false, handler: this._onApiGetDocumentInformation.bind(this)}]
+ ['getDocumentInformation', {async: false, handler: this._onApiGetDocumentInformation.bind(this)}],
+ ['getFrameSize', {async: true, handler: this._onApiGetFrameSize.bind(this)}],
+ ['setFrameSize', {async: true, handler: this._onApiSetFrameSize.bind(this)}]
]);
this._updateContentScale();
@@ -203,6 +205,14 @@ class Frontend {
return await this._popupFactory.clearAllVisibleOverride(token);
}
+ async _onApiGetFrameSize() {
+ return await this._popup.getFrameSize();
+ }
+
+ async _onApiSetFrameSize({width, height}) {
+ return await this._popup.setFrameSize(width, height);
+ }
+
// Private
_onResize() {
diff --git a/ext/fg/js/popup-factory.js b/ext/fg/js/popup-factory.js
index 252bcdf4..9a6c2ccd 100644
--- a/ext/fg/js/popup-factory.js
+++ b/ext/fg/js/popup-factory.js
@@ -48,7 +48,9 @@ class PopupFactory {
['clearAutoPlayTimer', {async: false, handler: this._onApiClearAutoPlayTimer.bind(this)}],
['setContentScale', {async: false, handler: this._onApiSetContentScale.bind(this)}],
['updateTheme', {async: false, handler: this._onApiUpdateTheme.bind(this)}],
- ['setCustomOuterCss', {async: false, handler: this._onApiSetCustomOuterCss.bind(this)}]
+ ['setCustomOuterCss', {async: false, handler: this._onApiSetCustomOuterCss.bind(this)}],
+ ['popup.getFrameSize', {async: true, handler: this._onApiGetFrameSize.bind(this)}],
+ ['popup.setFrameSize', {async: true, handler: this._onApiSetFrameSize.bind(this)}]
]);
}
@@ -265,6 +267,16 @@ class PopupFactory {
return popup.setCustomOuterCss(css, useWebExtensionApi);
}
+ async _onApiGetFrameSize({id}) {
+ const popup = this._getPopup(id);
+ return await popup.getFrameSize();
+ }
+
+ async _onApiSetFrameSize({id, width, height}) {
+ const popup = this._getPopup(id);
+ return await popup.setFrameSize(width, height);
+ }
+
// Private functions
_getPopup(id) {
diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js
index 26dce0d1..a03b58e8 100644
--- a/ext/fg/js/popup-proxy.js
+++ b/ext/fg/js/popup-proxy.js
@@ -149,6 +149,14 @@ class PopupProxy extends EventDispatcher {
return new DOMRect(0, 0, 0, 0);
}
+ getFrameSize() {
+ return this._invokeSafe('popup.getFrameSize', {id: this._id}, {width: 0, height: 0, valid: false});
+ }
+
+ setFrameSize(width, height) {
+ return this._invokeSafe('popup.setFrameSize', {id: this._id, width, height});
+ }
+
// Private
_invoke(action, params={}) {
diff --git a/ext/fg/js/popup-window.js b/ext/fg/js/popup-window.js
index 3d707a9e..3d68beb8 100644
--- a/ext/fg/js/popup-window.js
+++ b/ext/fg/js/popup-window.js
@@ -132,6 +132,14 @@ class PopupWindow extends EventDispatcher {
return new DOMRect(0, 0, 0, 0);
}
+ async getFrameSize() {
+ return {width: 0, height: 0, valid: false};
+ }
+
+ async setFrameSize(_width, _height) {
+ return false;
+ }
+
// Private
async _invoke(open, action, params={}, defaultReturnValue) {
diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js
index ffd72dca..c24ffb5c 100644
--- a/ext/fg/js/popup.js
+++ b/ext/fg/js/popup.js
@@ -208,6 +208,16 @@ class Popup extends EventDispatcher {
return this._frame.getBoundingClientRect();
}
+ async getFrameSize() {
+ const rect = this._frame.getBoundingClientRect();
+ return {width: rect.width, height: rect.height, valid: true};
+ }
+
+ async setFrameSize(width, height) {
+ this._setFrameSize(width, height);
+ return true;
+ }
+
// Private functions
_onFrameMouseOver() {
@@ -397,8 +407,7 @@ class Popup extends EventDispatcher {
frame.style.left = `${x}px`;
frame.style.top = `${y}px`;
- frame.style.width = `${width}px`;
- frame.style.height = `${height}px`;
+ this._setFrameSize(width, height);
this._setVisible(true);
if (this._child !== null) {
@@ -406,6 +415,12 @@ class Popup extends EventDispatcher {
}
}
+ _setFrameSize(width, height) {
+ const {style} = this._frame;
+ style.width = `${width}px`;
+ style.height = `${height}px`;
+ }
+
_setVisible(visible) {
this._visible.defaultValue = visible;
}
diff --git a/ext/mixed/css/display.css b/ext/mixed/css/display.css
index 605c0148..0a676a8f 100644
--- a/ext/mixed/css/display.css
+++ b/ext/mixed/css/display.css
@@ -1119,6 +1119,52 @@ button.action-button {
}
+/* Frame resizer */
+.frame-resizer-container {
+ position: fixed;
+ bottom: 0;
+ right: 0;
+ z-index: 100;
+ background: transparent;
+ pointer-events: none;
+ user-select: none;
+}
+.frame-resizer-sizer1 {
+ padding-top: 100%;
+ line-height: 0;
+}
+.frame-resizer-sizer2 {
+ display: inline-block;
+ overflow-x: hidden;
+ overflow-y: scroll;
+ vertical-align: bottom;
+}
+.frame-resizer-sizer2.frame-resizer-sizer2-with-min-size {
+ min-width: 1em;
+}
+.frame-resizer-svg {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ width: 75%;
+ height: 75%;
+}
+.frame-resizer-handle {
+ fill: var(--default-text-color);
+ opacity: 0.125;
+ cursor: se-resize;
+ pointer-events: auto;
+ transition:
+ fill var(--animation-duration) linear,
+ opacity var(--animation-duration) linear;
+}
+.frame-resizer-handle:hover {
+ fill: var(--accent-color);
+ opacity: 1;
+}
+
+
/* Conditional styles */
:root:not([data-enable-search-tags=true]) .tag[data-category=search] {
display: none;
@@ -1221,3 +1267,7 @@ button.action-button {
:root:not([data-glossary-layout-mode=compact]) .term-glossary-image-description {
display: block;
}
+
+:root[data-popup-display-mode=full-width] .frame-resizer-container {
+ display: none;
+}
diff --git a/ext/mixed/css/popup-outer.css b/ext/mixed/css/popup-outer.css
index 74307d9f..0ee8df9e 100644
--- a/ext/mixed/css/popup-outer.css
+++ b/ext/mixed/css/popup-outer.css
@@ -22,7 +22,7 @@ iframe.yomichan-popup {
border: 1em solid #999999;
box-shadow: 0 0 10em rgba(0, 0, 0, 0.5);
position: fixed;
- resize: both;
+ resize: none;
visibility: hidden;
z-index: 2147483647;
box-sizing: border-box;
@@ -38,7 +38,6 @@ iframe.yomichan-popup[data-outer-theme=auto][data-site-color=dark] {
iframe.yomichan-popup[data-popup-display-mode=full-width] {
border-left: none;
border-right: none;
- resize: none;
}
iframe.yomichan-popup[data-popup-display-mode=full-width][data-below=true] {
border-bottom: none;
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index b9daa9a0..b19d07e2 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -109,6 +109,11 @@ class Display extends EventDispatcher {
this._browser = null;
this._copyTextarea = null;
this._definitionTextScanner = null;
+ this._frameResizeToken = null;
+ this._frameResizeHandle = document.querySelector('#frame-resizer-handle');
+ this._frameResizeStartSize = null;
+ this._frameResizeStartOffset = null;
+ this._frameResizeEventListeners = new EventListenerCollection();
this.registerActions([
['close', () => { this.onEscape(); }],
@@ -228,6 +233,10 @@ class Display extends EventDispatcher {
this._navigationNextButton.addEventListener('click', this._onNextTermView.bind(this), false);
}
+ if (this._frameResizeHandle !== null) {
+ this._frameResizeHandle.addEventListener('mousedown', this._onFrameResizerMouseDown.bind(this), false);
+ }
+
// Final preparation
this._updateFocusedElement();
}
@@ -801,6 +810,7 @@ class Display extends EventDispatcher {
data.showPitchAccentPositionNotation = `${options.general.showPitchAccentPositionNotation}`;
data.showPitchAccentGraph = `${options.general.showPitchAccentGraph}`;
data.debug = `${options.general.debugInfo}`;
+ data.popupDisplayMode = `${options.general.popupDisplayMode}`;
}
_updateTheme(themeName) {
@@ -1795,4 +1805,68 @@ class Display extends EventDispatcher {
this._definitionTextScanner.clearSelection(true);
this.setContent(details);
}
+
+ _onFrameResizerMouseDown(e) {
+ if (e.button !== 0) { return; }
+ // Don't do e.preventDefault() here; this allows mousemove events to be processed
+ // if the pointer moves out of the frame.
+ this._startFrameResize(e);
+ }
+
+ _onFrameResizerMouseUp() {
+ this._stopFrameResize();
+ }
+
+ _onFrameResizerWindowBlur() {
+ this._stopFrameResize();
+ }
+
+ _onFrameResizerMouseMove(e) {
+ if ((e.buttons & 0x1) === 0x0) {
+ this._stopFrameResize();
+ } else {
+ if (this._frameResizeStartSize === null) { return; }
+ const {clientX: x, clientY: y} = e;
+ this._updateFrameSize(x, y);
+ }
+ }
+
+ _startFrameResize(e) {
+ if (this._frameResizeToken !== null) { return; }
+
+ const {clientX: x, clientY: y} = e;
+ const token = {};
+ this._frameResizeToken = token;
+ this._frameResizeStartOffset = {x, y};
+ this._frameResizeEventListeners.addEventListener(window, 'mouseup', this._onFrameResizerMouseUp.bind(this), false);
+ this._frameResizeEventListeners.addEventListener(window, 'blur', this._onFrameResizerWindowBlur.bind(this), false);
+ this._frameResizeEventListeners.addEventListener(window, 'mousemove', this._onFrameResizerMouseMove.bind(this), false);
+
+ this._initializeFrameResize(token);
+ }
+
+ async _initializeFrameResize(token) {
+ const size = await this._invokeOwner('getFrameSize');
+ if (this._frameResizeToken !== token) { return; }
+ this._frameResizeStartSize = size;
+ }
+
+ _stopFrameResize() {
+ if (this._frameResizeToken === null) { return; }
+
+ this._frameResizeEventListeners.removeAllEventListeners();
+ this._frameResizeStartSize = null;
+ this._frameResizeStartOffset = null;
+ this._frameResizeToken = null;
+ }
+
+ async _updateFrameSize(x, y) {
+ const handleSize = this._frameResizeHandle.getBoundingClientRect();
+ let {width, height} = this._frameResizeStartSize;
+ width += x - this._frameResizeStartOffset.x;
+ height += y - this._frameResizeStartOffset.y;
+ width = Math.max(Math.max(0, handleSize.width), width);
+ height = Math.max(Math.max(0, handleSize.height), height);
+ await this._invokeOwner('setFrameSize', {width, height});
+ }
}