v0.51.0: the web mix player has previous and next buttons
This commit is contained in:
@@ -659,7 +659,8 @@ persistence) → GUI (Qt widgets that read the manager and connect to its signal
|
||||
nobody can answer a prompt. Steps: `ServerCheckWorker` (`ssh test -d`; an
|
||||
existing folder asks before replacing it), `ExportWorker`, then
|
||||
`UploadWorker`, all shown in `gui/web_upload_progress.py`. The web
|
||||
player has a **shuffle** button (and the S key): a shuffled walk that starts at
|
||||
player has **previous/next** buttons (the arrow keys, wrapping at either
|
||||
end, through the shuffle order when on) and a **shuffle** button (and the S key): a shuffled walk that starts at
|
||||
the current song and plays every song once. A mix can carry several
|
||||
images (`plan.images`): the dialog always keeps one empty row to add
|
||||
another, and the page shows them in order under the description, a blank
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""LinTunes — iTunes-style music library manager and player for Linux."""
|
||||
|
||||
__version__ = "0.50.0"
|
||||
__version__ = "0.51.0"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
.audiojs audio { position: absolute; left: -1px; }
|
||||
|
||||
.audiojs {
|
||||
width: 286px;
|
||||
width: 334px;
|
||||
height: 36px;
|
||||
background: #eee;
|
||||
color: #000;
|
||||
@@ -40,9 +40,9 @@
|
||||
}
|
||||
|
||||
.audiojs .play-pause {
|
||||
width: 40px;
|
||||
width: 36px;
|
||||
height: 40px;
|
||||
padding: 4px 6px;
|
||||
padding: 4px 2px;
|
||||
margin: 0px;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
@@ -88,6 +88,21 @@
|
||||
.audiojs .time em { padding: 0px 2px 0px 0px; color: #000; font-style: normal; }
|
||||
.audiojs .time strong { padding: 0px 0px 0px 2px; font-weight: normal; }
|
||||
|
||||
/* Previous / next, either side of play-pause: plain black glyphs. */
|
||||
.audiojs .skip {
|
||||
float: left;
|
||||
width: 24px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #000;
|
||||
cursor: pointer;
|
||||
}
|
||||
.audiojs .prev { margin-left: 4px; }
|
||||
.audiojs .skip:hover { color: var(--accent, #8c764a); }
|
||||
.audiojs .skip:focus-visible { outline: 1px dotted #000; }
|
||||
|
||||
/* Shuffle: black like the other glyphs while off, the accent while on
|
||||
* (the same fill as the progress bar, with the text color picked for it). */
|
||||
.audiojs .shuffle {
|
||||
|
||||
@@ -24,10 +24,20 @@
|
||||
"use strict";
|
||||
|
||||
var WRAPPER =
|
||||
'<div class="skip prev" role="button" tabindex="0" title="Previous (←)">' +
|
||||
'<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">' +
|
||||
'<path d="M6 5h2.5v14H6zM20 5v14L9 12z" fill="currentColor"/>' +
|
||||
'</svg>' +
|
||||
'</div>' +
|
||||
'<div class="play-pause">' +
|
||||
'<p class="play"></p><p class="pause"></p>' +
|
||||
'<p class="loading"></p><p class="error"></p>' +
|
||||
'</div>' +
|
||||
'<div class="skip next" role="button" tabindex="0" title="Next (→)">' +
|
||||
'<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">' +
|
||||
'<path d="M15.5 5H18v14h-2.5zM4 5v14l11-7z" fill="currentColor"/>' +
|
||||
'</svg>' +
|
||||
'</div>' +
|
||||
'<div class="scrubber"><div class="progress"></div><div class="loaded"></div></div>' +
|
||||
'<div class="time"><em class="played">00:00</em>/<strong class="duration">00:00</strong></div>' +
|
||||
'<div class="error-message"></div>' +
|
||||
@@ -62,7 +72,9 @@
|
||||
played: wrap.querySelector(".played"),
|
||||
duration: wrap.querySelector(".duration"),
|
||||
error: wrap.querySelector(".error-message"),
|
||||
shuffle: wrap.querySelector(".shuffle")
|
||||
shuffle: wrap.querySelector(".shuffle"),
|
||||
prev: wrap.querySelector(".prev"),
|
||||
next: wrap.querySelector(".next")
|
||||
};
|
||||
|
||||
function state(name) {
|
||||
@@ -127,6 +139,8 @@
|
||||
return {
|
||||
element: audio,
|
||||
shuffleButton: ui.shuffle,
|
||||
prevButton: ui.prev,
|
||||
nextButton: ui.next,
|
||||
load: function (src) {
|
||||
ui.progress.style.width = "0%";
|
||||
ui.loaded.style.width = "0%";
|
||||
@@ -176,16 +190,30 @@
|
||||
function first() { return (order || items)[0]; }
|
||||
function last() { var list = order || items; return list[list.length - 1]; }
|
||||
|
||||
player.shuffleButton.addEventListener("click", function () {
|
||||
setShuffle(!order);
|
||||
});
|
||||
player.shuffleButton.addEventListener("keydown", function (e) {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setShuffle(!order);
|
||||
}
|
||||
});
|
||||
// The previous/next buttons and the arrow keys: wrap around at either
|
||||
// end, through the shuffled walk when shuffle is on.
|
||||
function goNext() {
|
||||
var current = document.querySelector("ol li.playing");
|
||||
select((current && step(current, 1)) || first(), true);
|
||||
}
|
||||
function goPrev() {
|
||||
var current = document.querySelector("ol li.playing");
|
||||
select((current && step(current, -1)) || last(), true);
|
||||
}
|
||||
|
||||
function button(el, action) {
|
||||
el.addEventListener("click", action);
|
||||
el.addEventListener("keydown", function (e) {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
action();
|
||||
}
|
||||
});
|
||||
}
|
||||
button(player.shuffleButton, function () { setShuffle(!order); });
|
||||
button(player.prevButton, goPrev);
|
||||
button(player.nextButton, goNext);
|
||||
|
||||
function select(li, autoplay) {
|
||||
items.forEach(function (other) { other.classList.remove("playing"); });
|
||||
@@ -215,13 +243,12 @@
|
||||
var tag = (target.tagName || "").toLowerCase();
|
||||
if (tag === "input" || tag === "textarea" || target.isContentEditable) return;
|
||||
|
||||
var current = document.querySelector("ol li.playing");
|
||||
if (e.key === "ArrowRight") {
|
||||
e.preventDefault();
|
||||
select((current && step(current, 1)) || first(), true);
|
||||
goNext();
|
||||
} else if (e.key === "ArrowLeft") {
|
||||
e.preventDefault();
|
||||
select((current && step(current, -1)) || last(), true);
|
||||
goPrev();
|
||||
} else if ((e.key === "s" || e.key === "S") && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
setShuffle(!order);
|
||||
|
||||
@@ -565,3 +565,11 @@ def test_upload_window_shows_the_size(qapp, window):
|
||||
_wait_for(qapp, lambda: _FakeUpload.runs)
|
||||
from lintunes.gui import web_upload_progress as wp
|
||||
assert "KB" in window._web_progress.status(wp.EXPORT)
|
||||
|
||||
|
||||
def test_player_ships_previous_and_next_buttons():
|
||||
js = (exporter.templates() / "player.js").read_text()
|
||||
css = (exporter.templates() / "player.css").read_text()
|
||||
assert 'class="skip prev"' in js and 'class="skip next"' in js
|
||||
assert "button(player.prevButton, goPrev)" in js
|
||||
assert ".audiojs .skip" in css
|
||||
|
||||
Reference in New Issue
Block a user