mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-20 15:27:32 +09:00
REALLY finish off PiP
This commit is contained in:
parent
55a53a2576
commit
14b7011d74
4 changed files with 196 additions and 27 deletions
|
|
@ -11,14 +11,53 @@ browser[remote="true"] {
|
|||
min-height: 170px;
|
||||
background: black;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#player-stack {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
#player-container,
|
||||
#player-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#player-container {
|
||||
background: black;
|
||||
}
|
||||
|
||||
#player-container > browser {
|
||||
display: -moz-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#player-overlay {
|
||||
-moz-window-dragging: drag;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
-moz-window-dragging: no-drag;
|
||||
}
|
||||
|
||||
#drag-area {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.pip-controls {
|
||||
|
|
@ -29,22 +68,33 @@ browser[remote="true"] {
|
|||
|
||||
#player-overlay:hover .pip-controls,
|
||||
#player-overlay[keyboard] .pip-controls,
|
||||
#player-overlay[seeking] .pip-controls {
|
||||
#player-overlay[seeking] .pip-controls,
|
||||
#player-stack[dragging] .pip-controls {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#top-controls {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: flex-end;
|
||||
padding: 8px;
|
||||
background: linear-gradient(rgba(0, 0, 0, .55), transparent);
|
||||
}
|
||||
|
||||
#playback-controls {
|
||||
flex-shrink: 0;
|
||||
padding: 12px 10px 8px;
|
||||
background: linear-gradient(transparent, rgba(0, 0, 0, .85));
|
||||
-moz-window-dragging: no-drag;
|
||||
}
|
||||
|
||||
#button-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pip-controls button {
|
||||
-moz-appearance: none;
|
||||
-moz-window-dragging: no-drag;
|
||||
|
|
@ -72,6 +122,7 @@ browser[remote="true"] {
|
|||
}
|
||||
|
||||
#seek {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0 0 6px;
|
||||
|
|
@ -85,20 +136,56 @@ browser[remote="true"] {
|
|||
}
|
||||
|
||||
#time {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin: 0 6px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
#resize {
|
||||
justify-self: end;
|
||||
align-self: end;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
cursor: se-resize;
|
||||
.resize-edge,
|
||||
.resize-corner {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
-moz-window-dragging: no-drag;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#resize:hover {
|
||||
opacity: 1;
|
||||
.resize-edge[data-resize="n"],
|
||||
.resize-edge[data-resize="s"] {
|
||||
left: 14px;
|
||||
right: 14px;
|
||||
height: 6px;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
.resize-edge[data-resize="e"],
|
||||
.resize-edge[data-resize="w"] {
|
||||
top: 14px;
|
||||
bottom: 14px;
|
||||
width: 6px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.resize-corner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
[data-resize*="n"] { top: 0; }
|
||||
[data-resize*="s"] { bottom: 0; }
|
||||
[data-resize*="e"] { right: 0; }
|
||||
[data-resize*="w"] { left: 0; }
|
||||
[data-resize="nw"], [data-resize="se"] { cursor: nwse-resize; }
|
||||
[data-resize="ne"], [data-resize="sw"] { cursor: nesw-resize; }
|
||||
|
||||
@media (min-width: 700px) {
|
||||
#playback-controls {
|
||||
padding: 20px 20px 14px;
|
||||
}
|
||||
.pip-controls button {
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
#time {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,71 @@ var Player = {
|
|||
sourceTab: null,
|
||||
seeking: false,
|
||||
state: null,
|
||||
drag: null,
|
||||
|
||||
startDrag(event) {
|
||||
if (event.button != 0 ||
|
||||
event.target.closest("button, input, #playback-controls")) {
|
||||
return;
|
||||
}
|
||||
let stack = document.getElementById("player-stack");
|
||||
if (!stack.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
this.drag = {
|
||||
direction: event.target.getAttribute("data-resize") || "",
|
||||
pointerX: event.screenX,
|
||||
pointerY: event.screenY,
|
||||
x: window.screenX,
|
||||
y: window.screenY,
|
||||
width: window.outerWidth,
|
||||
height: window.outerHeight,
|
||||
};
|
||||
// Capture keeps the gesture working after the pointer leaves the window.
|
||||
stack.setCapture(true);
|
||||
stack.setAttribute("dragging", "true");
|
||||
},
|
||||
|
||||
moveDrag(event) {
|
||||
let drag = this.drag;
|
||||
if (!drag) {
|
||||
return;
|
||||
}
|
||||
if (!(event.buttons & 1)) {
|
||||
this.endDrag();
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
let dx = event.screenX - drag.pointerX;
|
||||
let dy = event.screenY - drag.pointerY;
|
||||
if (!drag.direction) {
|
||||
window.moveTo(drag.x + dx, drag.y + dy);
|
||||
return;
|
||||
}
|
||||
let west = drag.direction.includes("w");
|
||||
let east = drag.direction.includes("e");
|
||||
let north = drag.direction.includes("n");
|
||||
let south = drag.direction.includes("s");
|
||||
let width = Math.max(300, drag.width + (west ? -dx : east ? dx : 0));
|
||||
let height = Math.max(170, drag.height + (north ? -dy : south ? dy : 0));
|
||||
window.resizeTo(width, height);
|
||||
// Anchor the opposite edge, using the actual size in case the platform
|
||||
// imposed a constraint (for example, at a monitor boundary).
|
||||
if (west || north) {
|
||||
window.moveTo(west ? drag.x + drag.width - window.outerWidth : drag.x,
|
||||
north ? drag.y + drag.height - window.outerHeight : drag.y);
|
||||
}
|
||||
},
|
||||
|
||||
endDrag() {
|
||||
if (this.drag) {
|
||||
this.drag = null;
|
||||
let stack = document.getElementById("player-stack");
|
||||
stack.releaseCapture();
|
||||
stack.removeAttribute("dragging");
|
||||
}
|
||||
},
|
||||
|
||||
init() {
|
||||
if (this.browser) {
|
||||
|
|
@ -129,6 +194,7 @@ var Player = {
|
|||
},
|
||||
|
||||
destroy() {
|
||||
this.endDrag();
|
||||
if (this.sourceTab) {
|
||||
this.sourceTab.removeEventListener("TabClose", this.close);
|
||||
this.source.removeEventListener("oop-browser-crashed", this.close);
|
||||
|
|
@ -171,6 +237,14 @@ window.addEventListener("keydown", event => {
|
|||
}
|
||||
}
|
||||
});
|
||||
window.addEventListener("mousemove", () => {
|
||||
window.addEventListener("mousedown", event => Player.startDrag(event));
|
||||
window.addEventListener("mousemove", event => {
|
||||
Player.moveDrag(event);
|
||||
document.getElementById("player-overlay").removeAttribute("keyboard");
|
||||
});
|
||||
window.addEventListener("mouseup", () => Player.endDrag());
|
||||
window.addEventListener("blur", event => {
|
||||
if (event.target == window) {
|
||||
Player.endDrag();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,28 +12,35 @@
|
|||
hidechrome="true"
|
||||
persist="screenX screenY width height">
|
||||
<script type="application/javascript" src="chrome://browser/content/pictureInPicture.js"/>
|
||||
<stack flex="1">
|
||||
<vbox id="player-container" flex="1"/>
|
||||
<vbox id="player-overlay" flex="1">
|
||||
<hbox class="pip-controls" id="top-controls" pack="end">
|
||||
<html:div id="player-stack">
|
||||
<html:div id="player-container"/>
|
||||
<html:div id="player-overlay">
|
||||
<html:div class="pip-controls" id="top-controls">
|
||||
<button id="close" label="×" tooltiptext="&pictureInPicture.close;"
|
||||
aria-label="&pictureInPicture.close;" oncommand="window.close();"/>
|
||||
</hbox>
|
||||
<spacer flex="1"/>
|
||||
<vbox class="pip-controls" id="playback-controls">
|
||||
</html:div>
|
||||
<html:div id="drag-area"/>
|
||||
<html:div class="pip-controls" id="playback-controls">
|
||||
<html:input id="seek" type="range" min="0" max="1" step="0.1" value="0"
|
||||
disabled="disabled" aria-label="&pictureInPicture.seek;"
|
||||
oninput="Player.seek(this.value);" onchange="Player.finishSeeking();"/>
|
||||
<hbox align="center">
|
||||
<html:div id="button-row">
|
||||
<button id="play" label="&mediaPlay.label;" oncommand="Player.command('playpause');"/>
|
||||
<button id="mute" label="&mediaMute.label;" oncommand="Player.command('mute');"/>
|
||||
<label id="time" value="0:00 / 0:00" flex="1" crop="end"/>
|
||||
<button id="return" label="&pictureInPicture.return;" oncommand="Player.returnToTab();"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</vbox>
|
||||
<resizer id="resize" dir="bottomright" right="0" bottom="0"/>
|
||||
</stack>
|
||||
</html:div>
|
||||
</html:div>
|
||||
</html:div>
|
||||
<html:div class="resize-edge" data-resize="n"/>
|
||||
<html:div class="resize-edge" data-resize="s"/>
|
||||
<html:div class="resize-edge" data-resize="e"/>
|
||||
<html:div class="resize-edge" data-resize="w"/>
|
||||
<html:div class="resize-corner" data-resize="nw"/>
|
||||
<html:div class="resize-corner" data-resize="ne"/>
|
||||
<html:div class="resize-corner" data-resize="sw"/>
|
||||
<html:div class="resize-corner" data-resize="se"/>
|
||||
</html:div>
|
||||
<label id="pause-label" value="&mediaPause.label;" hidden="true"/>
|
||||
<label id="unmute-label" value="&mediaUnmute.label;" hidden="true"/>
|
||||
<label id="live-label" value="&pictureInPicture.live;" hidden="true"/>
|
||||
|
|
|
|||
|
|
@ -107,12 +107,13 @@ addMessageListener("PictureInPicture:Init", message => {
|
|||
source = PictureInPicture.takeSource(message.data.id);
|
||||
sourceWindow = source.ownerGlobal;
|
||||
let doc = content.document;
|
||||
doc.documentElement.style.cssText = "height:100%;background:black";
|
||||
doc.body.style.cssText = "margin:0;height:100%;overflow:hidden";
|
||||
doc.documentElement.style.cssText = "width:100%;height:100%;background:black;overflow:hidden";
|
||||
doc.body.style.cssText = "margin:0;width:100%;height:100%;overflow:hidden";
|
||||
output = doc.createElement("video");
|
||||
still = doc.createElement("canvas");
|
||||
for (let element of [output, still]) {
|
||||
element.style.cssText = "position:absolute;width:100%;height:100%;object-fit:contain";
|
||||
element.style.cssText = "position:fixed;left:0;top:0;width:100vw;height:100vh;" +
|
||||
"max-width:none;max-height:none;object-fit:contain";
|
||||
doc.body.appendChild(element);
|
||||
}
|
||||
// Decoder capture redirects audio to the captured stream. Its samples
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue