Files
clapper/src/playlist.js
Rafał Dzięgiel fe03719b38 Show tooltip with full playlist item text on hover
Some titles might be more than few words and will not fit in current playlist popover. Instead of stretching it, show full playlist item filename (or path) on hover in a tooltip.
2021-04-18 18:44:16 +02:00

230 lines
5.6 KiB
JavaScript

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();
if(itemIndex === this.activeRowId) {
const root = this.get_root();
root.emit('close-request');
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;
}
getActiveRow()
{
return this.get_row_at_index(this.activeRowId);
}
getActiveFilename()
{
const row = this.getActiveRow();
if(!row) return null;
return row.filename;
}
deactivateActiveItem()
{
if(this.activeRowId < 0)
return;
const row = this.getActiveRow();
if(!row) return null;
const icon = row.child.get_first_child();
const button = row.child.get_last_child();
icon.icon_name = 'open-menu-symbolic';
button.icon_name = 'list-remove-symbolic';
}
_onRowActivated(listBox, row)
{
const { player } = this.get_ancestor(Gtk.Grid);
const icon = row.child.get_first_child();
const button = row.child.get_last_child();
this.deactivateActiveItem();
icon.icon_name = 'media-playback-start-symbolic';
button.icon_name = 'window-close-symbolic';
this.activeRowId = row.get_index();
player.set_uri(row.uri);
}
});
let PlaylistItem = GObject.registerClass(
class ClapperPlaylistItem extends Gtk.ListBoxRow
{
_init(uri)
{
super._init({
/* TODO: Fix playlist navigation in fullscreen */
can_focus: false,
});
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;
this.set_tooltip_text(this.filename);
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: this.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: 'list-remove-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 = value.get_index();
if(destIndex === targetIndex)
return true;
const listBox = this.get_ancestor(Gtk.ListBox);
if(listBox && destIndex >= 0) {
listBox.remove(value);
listBox.insert(value, destIndex);
return true;
}
return false;
}
});