Add playlist widget to elapsed time button popover

This commit is contained in:
Rafostar
2021-01-19 14:40:25 +01:00
parent fca7966ece
commit 3ba21d42ec
8 changed files with 345 additions and 118 deletions

View File

@@ -17,7 +17,6 @@ class ClapperApp extends AppBase
this.get_flags()
| Gio.ApplicationFlags.HANDLES_OPEN
);
this.playlist = [];
}
vfunc_startup()
@@ -42,10 +41,12 @@ class ClapperApp extends AppBase
{
super.vfunc_open(files, hint);
this.playlist = files;
const { player } = this.active_window.get_child();
if(this.doneFirstActivate)
this.setWindowPlaylist(this.active_window);
if(!this.doneFirstActivate)
player._preparePlaylist(files);
else
player.set_playlist(files);
this.activate();
}
@@ -54,15 +55,12 @@ class ClapperApp extends AppBase
{
super._onWindowShow(window);
this.setWindowPlaylist(window);
}
const { player } = this.active_window.get_child();
const success = player.playlistWidget.nextTrack();
setWindowPlaylist(window)
{
if(!this.playlist.length)
return;
if(!success)
debug('playlist is empty');
const { player } = window.get_child();
player.set_playlist(this.playlist);
player.widget.grab_focus();
}
});

View File

@@ -85,13 +85,6 @@ class ClapperIconButton extends CustomButton
});
this.floatUnaffected = true;
}
setFullscreenMode(isFullscreen)
{
/* Redraw icon after style class change */
this.set_icon_name(this.icon_name);
super.setFullscreenMode(isFullscreen);
}
});
var IconToggleButton = GObject.registerClass(
@@ -116,38 +109,13 @@ class ClapperIconToggleButton extends IconButton
}
});
var LabelButton = GObject.registerClass(
class ClapperLabelButton extends CustomButton
var PopoverButtonBase = GObject.registerClass(
class ClapperPopoverButtonBase extends CustomButton
{
_init(text)
_init()
{
super._init({
margin_start: 0,
margin_end: 0,
});
super._init();
this.customLabel = new Gtk.Label({
label: text,
single_line_mode: true,
});
this.customLabel.add_css_class('labelbutton');
this.set_child(this.customLabel);
}
set_label(text)
{
this.customLabel.set_text(text);
}
});
var PopoverButton = GObject.registerClass(
class ClapperPopoverButton extends IconButton
{
_init(icon)
{
super._init(icon);
this.floatUnaffected = false;
this.popover = new Gtk.Popover({
position: Gtk.PositionType.TOP,
});
@@ -203,3 +171,59 @@ class ClapperPopoverButton extends IconButton
this.popover.unparent();
}
});
var IconPopoverButton = GObject.registerClass(
class ClapperIconPopoverButton extends PopoverButtonBase
{
_init(icon)
{
super._init();
this.icon_name = icon;
}
});
var LabelPopoverButton = GObject.registerClass(
class ClapperLabelPopoverButton extends PopoverButtonBase
{
_init(text)
{
super._init();
this.customLabel = new Gtk.Label({
label: text,
single_line_mode: true,
});
this.customLabel.add_css_class('labelbutton');
this.set_child(this.customLabel);
}
set_label(text)
{
this.customLabel.set_text(text);
}
});
var ElapsedPopoverButton = GObject.registerClass(
class ClapperElapsedPopoverButton extends LabelPopoverButton
{
_init(text)
{
super._init(text);
this.scrolledWindow = new Gtk.ScrolledWindow({
max_content_height: 150,
min_content_width: 250,
propagate_natural_height: true,
});
this.popoverBox.append(this.scrolledWindow);
}
setFullscreenMode(isFullscreen)
{
super.setFullscreenMode(isFullscreen);
this.scrolledWindow.max_content_height = (isFullscreen)
? 190 : 150;
}
});

View File

@@ -42,7 +42,7 @@ class ClapperControls extends Gtk.Box
this._addTogglePlayButton();
const elapsedRevealer = new Revealers.ButtonsRevealer('SLIDE_RIGHT');
this.elapsedButton = this.addLabelButton('00:00/00:00', elapsedRevealer);
this.elapsedButton = this.addElapsedPopoverButton('00:00/00:00', elapsedRevealer);
elapsedRevealer.set_reveal_child(true);
this.revealersArr.push(elapsedRevealer);
this.append(elapsedRevealer);
@@ -59,22 +59,22 @@ class ClapperControls extends Gtk.Box
const tracksRevealer = new Revealers.ButtonsRevealer(
'SLIDE_LEFT', revealTracksButton
);
this.visualizationsButton = this.addPopoverButton(
this.visualizationsButton = this.addIconPopoverButton(
'display-projector-symbolic',
tracksRevealer
);
this.visualizationsButton.set_visible(false);
this.videoTracksButton = this.addPopoverButton(
this.videoTracksButton = this.addIconPopoverButton(
'emblem-videos-symbolic',
tracksRevealer
);
this.videoTracksButton.set_visible(false);
this.audioTracksButton = this.addPopoverButton(
this.audioTracksButton = this.addIconPopoverButton(
'emblem-music-symbolic',
tracksRevealer
);
this.audioTracksButton.set_visible(false);
this.subtitleTracksButton = this.addPopoverButton(
this.subtitleTracksButton = this.addIconPopoverButton(
'media-view-subtitles-symbolic',
tracksRevealer
);
@@ -165,17 +165,25 @@ class ClapperControls extends Gtk.Box
return button;
}
addLabelButton(text, revealer)
addIconPopoverButton(iconName, revealer)
{
text = text || '';
const button = new Buttons.LabelButton(text);
const button = new Buttons.IconPopoverButton(iconName);
return this.addButton(button, revealer);
}
addPopoverButton(iconName, revealer)
addLabelPopoverButton(text, revealer)
{
const button = new Buttons.PopoverButton(iconName);
text = text || '';
const button = new Buttons.LabelPopoverButton(text);
return this.addButton(button, revealer);
}
addElapsedPopoverButton(text, revealer)
{
text = text || '';
const button = new Buttons.ElapsedPopoverButton(text);
return this.addButton(button, revealer);
}
@@ -363,7 +371,7 @@ class ClapperControls extends Gtk.Box
_addVolumeButton()
{
this.volumeButton = this.addPopoverButton(
this.volumeButton = this.addIconPopoverButton(
'audio-volume-muted-symbolic'
);
this.volumeScale = new Gtk.Scale({

View File

@@ -15,7 +15,6 @@ class ClapperPlayer extends PlayerBase
super._init();
this.cursorInPlayer = false;
this.is_local_file = false;
this.seek_done = true;
this.dragAllowed = false;
this.isWidgetDragging = false;
@@ -32,9 +31,6 @@ class ClapperPlayer extends PlayerBase
this._maxVolume = Misc.getLinearValue(Misc.maxVolume);
this._playlist = [];
this._trackId = 0;
this._hideCursorTimeout = null;
this._hideControlsTimeout = null;
this._updateTimeTimeout = null;
@@ -80,45 +76,24 @@ class ClapperPlayer extends PlayerBase
this._realizeSignal = this.widget.connect('realize', this._onWidgetRealize.bind(this));
}
set_media(source)
set_uri(uri)
{
let file;
if(source.get_path)
file = source;
else {
if(!Gst.uri_is_valid(source))
source = Gst.filename_to_uri(source);
if(!source)
return debug('parsing source to URI failed');
debug(`parsed source to URI: ${source}`);
if(Gst.Uri.get_protocol(source) !== 'file') {
this.is_local_file = false;
return this.set_uri(source);
}
file = Gio.file_new_for_uri(source);
}
if(Gst.Uri.get_protocol(uri) !== 'file')
return super.set_uri(uri);
let file = Gio.file_new_for_uri(uri);
if(!file.query_exists(null)) {
debug(`file does not exist: ${file.get_path()}`, 'LEVEL_WARNING');
this._trackId++;
if(this._playlist.length <= this._trackId)
return debug('set media reached end of playlist');
if(!this.playlistWidget.nextTrack())
debug('set media reached end of playlist');
return this.set_media(this._playlist[this._trackId]);
return;
}
const uri = file.get_uri();
if(uri.endsWith('.claps'))
return this.load_playlist_file(file);
this.is_local_file = true;
this.set_uri(uri);
super.set_uri(uri);
}
load_playlist_file(file)
@@ -140,7 +115,7 @@ class ClapperPlayer extends PlayerBase
if(!lineFile)
continue;
line = lineFile.get_path();
line = lineFile.get_uri();
}
debug(`new playlist item: ${line}`);
playlist.push(line);
@@ -149,20 +124,29 @@ class ClapperPlayer extends PlayerBase
this.set_playlist(playlist);
}
set_playlist(playlist)
_preparePlaylist(playlist)
{
if(!Array.isArray(playlist) || !playlist.length)
return;
this.playlistWidget.removeAll();
this._trackId = 0;
this._playlist = playlist;
for(let source of playlist) {
const uri = (source.get_uri != null)
? source.get_uri()
: Gst.uri_is_valid(source)
? source
: Gst.filename_to_uri(source);
this.set_media(this._playlist[0]);
this.playlistWidget.addItem(uri);
}
}
get_playlist()
set_playlist(playlist)
{
return this._playlist;
this._preparePlaylist(playlist);
const firstTrack = this.playlistWidget.get_row_at_index(0);
if(!firstTrack) return;
firstTrack.activate();
}
set_subtitles(source)
@@ -433,14 +417,15 @@ class ClapperPlayer extends PlayerBase
_onStreamEnded(player)
{
debug(`end of stream: ${this._trackId}`);
this.emitWs('end_of_stream', this._trackId);
const lastTrackId = this.playlistWidget.activeRowId;
this._trackId++;
debug(`end of stream: ${lastTrackId}`);
this.emitWs('end_of_stream', lastTrackId);
if(this._trackId < this._playlist.length)
this.set_media(this._playlist[this._trackId]);
else if(settings.get_boolean('close-auto')) {
if(this.playlistWidget.nextTrack())
return;
if(settings.get_boolean('close-auto')) {
/* Stop will be automatically called soon afterwards */
this._performCloseCleanup(this.widget.get_root());
this.quitOnStop = true;

View File

@@ -1,6 +1,7 @@
const { Gio, GLib, GObject, Gst, GstPlayer, Gtk } = imports.gi;
const Debug = imports.clapper_src.debug;
const Misc = imports.clapper_src.misc;
const { PlaylistWidget } = imports.clapper_src.playlist;
const { WebApp } = imports.clapper_src.webApp;
const { debug } = Debug;
@@ -54,6 +55,7 @@ class ClapperPlayerBase extends GstPlayer.Player
this.webserver = null;
this.webapp = null;
this.playlistWidget = new PlaylistWidget();
this.set_all_plugins_ranks();
this.set_initial_config();

207
clapper_src/playlist.js Normal file
View File

@@ -0,0 +1,207 @@
const { Gdk, GLib, GObject, Gst, Gtk, Pango } = imports.gi;
var PlaylistWidget = GObject.registerClass(
class ClapperPlaylistWidget extends Gtk.ListBox
{
_init()
{
super._init({
selection_mode: Gtk.SelectionMode.NONE,
});
this.activeRowId = -1;
this.connect('row-activated', this._onRowActivated.bind(this));
}
addItem(uri)
{
const item = new PlaylistItem(uri);
this.append(item);
}
removeItem(item)
{
const itemIndex = item.get_index();
/* TODO: Handle this case somehow (should app quit?)
* or disable remove button */
if(itemIndex === this.activeRowId)
return;
if(itemIndex < this.activeRowId)
this.activeRowId--;
this.remove(item);
}
removeAll()
{
let oldItem;
while((oldItem = this.get_row_at_index(0)))
this.remove(oldItem);
this.activeRowId = -1;
}
nextTrack()
{
const nextRow = this.get_row_at_index(this.activeRowId + 1);
if(!nextRow)
return false;
nextRow.activate();
return true;
}
getActiveFilename()
{
const row = this.get_row_at_index(this.activeRowId);
if(!row) return null;
return row.filename;
}
/* FIXME: Remove once/if GstPlay(er) gets
* less vague MediaInfo signals */
getActiveIsLocalFile()
{
const row = this.get_row_at_index(this.activeRowId);
if(!row) return null;
return row.isLocalFile;
}
_onRowActivated(listBox, row)
{
const { player } = this.get_ancestor(Gtk.Grid);
this.activeRowId = row.get_index();
player.set_uri(row.uri);
}
});
let PlaylistItem = GObject.registerClass(
class ClapperPlaylistItem extends Gtk.ListBoxRow
{
_init(uri)
{
super._init();
this.uri = uri;
this.isLocalFile = false;
let filename;
if(Gst.Uri.get_protocol(uri) === 'file') {
filename = GLib.path_get_basename(
GLib.filename_from_uri(uri)[0]
);
this.isLocalFile = true;
}
this.filename = filename || uri;
const box = new Gtk.Box({
orientation: Gtk.Orientation.HORIZONTAL,
spacing: 6,
margin_start: 6,
margin_end: 6,
height_request: 22,
});
const icon = new Gtk.Image({
icon_name: 'open-menu-symbolic',
});
const label = new Gtk.Label({
label: filename,
single_line_mode: true,
ellipsize: Pango.EllipsizeMode.END,
width_chars: 5,
hexpand: true,
halign: Gtk.Align.START,
});
const button = new Gtk.Button({
icon_name: 'edit-delete-symbolic',
});
button.add_css_class('flat');
button.add_css_class('circular');
button.add_css_class('popoverbutton');
button.connect('clicked', this._onRemoveClicked.bind(this));
box.append(icon);
box.append(label);
box.append(button);
this.set_child(box);
/* FIXME: D&D inside popover is broken in GTK4
const dragSource = new Gtk.DragSource({
actions: Gdk.DragAction.MOVE
});
dragSource.connect('prepare', this._onDragPrepare.bind(this));
dragSource.connect('drag-begin', this._onDragBegin.bind(this));
dragSource.connect('drag-end', this._onDragEnd.bind(this));
this.add_controller(dragSource);
const dropTarget = new Gtk.DropTarget({
actions: Gdk.DragAction.MOVE,
preload: true,
});
dropTarget.set_gtypes([PlaylistItem]);
dropTarget.connect('enter', this._onEnter.bind(this));
dropTarget.connect('drop', this._onDrop.bind(this));
this.add_controller(dropTarget);
*/
}
_onRemoveClicked(button)
{
const listBox = this.get_ancestor(Gtk.ListBox);
listBox.removeItem(this);
}
_onDragPrepare(source, x, y)
{
const widget = source.get_widget();
const paintable = new Gtk.WidgetPaintable({ widget });
const staticImg = paintable.get_current_image();
source.set_icon(staticImg, x, y);
return Gdk.ContentProvider.new_for_value(widget);
}
_onDragBegin(source, drag)
{
this.child.set_opacity(0.3);
}
_onDragEnd(source, drag, deleteData)
{
this.child.set_opacity(1.0);
}
_onEnter(target, x, y)
{
return (target.value)
? Gdk.DragAction.MOVE
: 0;
}
_onDrop(target, value, x, y)
{
const destIndex = this.get_index();
const targetIndex = target.value.get_index();
if(destIndex === targetIndex)
return true;
const listBox = this.get_ancestor(Gtk.ListBox);
if(listBox && destIndex >= 0) {
listBox.remove(target.value);
listBox.insert(target.value, destIndex);
return true;
}
return false;
}
});

View File

@@ -1,4 +1,4 @@
const { Gdk, GLib, GObject, Gst, GstPlayer, Gtk } = imports.gi;
const { Gdk, GLib, GObject, GstPlayer, Gtk } = imports.gi;
const { Controls } = imports.clapper_src.controls;
const Debug = imports.clapper_src.debug;
const Misc = imports.clapper_src.misc;
@@ -45,6 +45,8 @@ class ClapperWidget extends Gtk.Grid
this.mapSignal = this.connect('map', this._onMap.bind(this));
this.player = new Player();
this.controls.elapsedButton.scrolledWindow.set_child(this.player.playlistWidget);
this.player.connect('position-updated', this._onPlayerPositionUpdated.bind(this));
this.player.connect('duration-changed', this._onPlayerDurationChanged.bind(this));
@@ -324,17 +326,10 @@ class ClapperWidget extends Gtk.Grid
updateTitles(mediaInfo)
{
let title = mediaInfo.get_title();
let subtitle = mediaInfo.get_uri();
let subtitle = this.player.playlistWidget.getActiveFilename();
if(Gst.Uri.get_protocol(subtitle) === 'file') {
subtitle = GLib.path_get_basename(
GLib.filename_from_uri(subtitle)[0]
);
}
if(!title) {
title = (!subtitle)
? this.defaultTitle
: (subtitle.includes('.'))
title = (subtitle.includes('.'))
? subtitle.split('.').slice(0, -1).join('.')
: subtitle;
@@ -461,7 +456,7 @@ class ClapperWidget extends Gtk.Grid
this.controls.positionScale.clear_marks();
this.controls.chapters = null;
}
if(!player.is_local_file) {
if(!player.playlistWidget.getActiveIsLocalFile()) {
this.needsTracksUpdate = true;
}
break;

View File

@@ -22,6 +22,10 @@ radio {
min-width: 17px;
min-height: 17px;
}
/* Adwaita is missing osd ListBox */
.osd list {
background: none;
}
.osd .playercontrols {
-gtk-icon-size: 24px;
}
@@ -70,6 +74,10 @@ radio {
font-weight: 600;
font-variant-numeric: tabular-nums;
}
.popoverbutton {
min-width: 24px;
min-height: 24px;
}
/* Position Scale */
.positionscale {