192 lines
6.5 KiB
Python
192 lines
6.5 KiB
Python
from typing import List, Any
|
|
|
|
from .. import util
|
|
from ..common import IconButton, IconToggleButton, SpinnerImage, SpinnerPicture
|
|
from .manager import Manager
|
|
from ...util import resolve_path
|
|
from ..actions import run_action
|
|
|
|
from gi.repository import Gdk, GdkPixbuf, GLib, GObject, Gtk, Pango, Handy
|
|
|
|
def create_play_button(manager: Manager, large=True, **kwargs):
|
|
button = IconButton(
|
|
"media-playback-start-symbolic",
|
|
"Play",
|
|
relief=large,
|
|
icon_size=Gtk.IconSize.LARGE_TOOLBAR,
|
|
**kwargs,
|
|
)
|
|
|
|
if large:
|
|
button.get_style_context().add_class("play-button-large")
|
|
|
|
button.set_action_name("app.play-pause")
|
|
manager.bind_property("has-song", button, "sensitive")
|
|
manager.bind_property("play-button-icon", button, "icon-name")
|
|
manager.bind_property("play-button-tooltip", button, "tooltip-text")
|
|
|
|
return button
|
|
|
|
def create_repeat_button(manager: Manager, **kwargs):
|
|
button = IconToggleButton("media-playlist-repeat", "Switch between repeat modes", **kwargs)
|
|
button.set_action_name("app.repeat")
|
|
manager.bind_property("repeat-button-icon", button, "icon-name")
|
|
|
|
def on_active_change(*_):
|
|
if manager.repeat_button_active != button.get_active():
|
|
# Don't run the action, just update visual state
|
|
button.set_action_name(None)
|
|
button.set_active(manager.repeat_button_active)
|
|
button.set_action_name("app.repeat")
|
|
manager.connect("notify::repeat-button-active", on_active_change)
|
|
|
|
return button
|
|
|
|
def create_shuffle_button(manager: Manager, **kwargs):
|
|
button = IconToggleButton("media-playlist-shuffle-symbolic", "Toggle playlist shuffling", **kwargs)
|
|
button.set_action_name("app.shuffle")
|
|
|
|
def on_active_change(*_):
|
|
if manager.shuffle_button_active != button.get_active():
|
|
# Don't run the action, just update visual state
|
|
button.set_action_name(None)
|
|
button.set_active(manager.shuffle_button_active)
|
|
button.set_action_name("app.shuffle")
|
|
manager.connect("notify::shuffle-button-active", on_active_change)
|
|
|
|
return button
|
|
|
|
def create_next_button(manager: Manager, **kwargs):
|
|
button = IconButton(
|
|
"media-skip-forward-symbolic",
|
|
"Go to next song",
|
|
icon_size=Gtk.IconSize.LARGE_TOOLBAR,
|
|
**kwargs,
|
|
)
|
|
button.set_action_name("app.next-track")
|
|
manager.bind_property("has-song", button, "sensitive")
|
|
|
|
return button
|
|
|
|
def create_prev_button(manager: Manager, **kwargs):
|
|
button = IconButton(
|
|
"media-skip-backward-symbolic",
|
|
"Go to previous song",
|
|
icon_size=Gtk.IconSize.LARGE_TOOLBAR,
|
|
**kwargs,
|
|
)
|
|
button.set_action_name("app.prev-track")
|
|
manager.bind_property("has-song", button, "sensitive")
|
|
|
|
return button
|
|
|
|
def create_label(manager: Manager, property: str, **kwargs):
|
|
label = Gtk.Label(**kwargs)
|
|
manager.bind_property(property, label, "label")
|
|
return label
|
|
|
|
def show_play_queue_popover(manager: Manager, relative_to, paths: List[str], pos_x: int, pos_y: int):
|
|
song_ids = [manager.play_queue_store[p][-1] for p in paths]
|
|
|
|
remove_text = (
|
|
"Remove " + util.pluralize("song", len(song_ids)) + " from queue"
|
|
)
|
|
|
|
def on_download_state_change(song_id: str):
|
|
# Refresh the entire window (no force) because the song could
|
|
# be in a list anywhere in the window.
|
|
manager.emit("refresh-window", {}, False)
|
|
|
|
def on_remove_songs_click(_: Any):
|
|
manager.emit("songs-removed", [p.get_indices()[0] for p in paths])
|
|
|
|
util.show_song_popover(
|
|
song_ids,
|
|
pos_x,
|
|
pos_y,
|
|
relative_to,
|
|
manager.offline_mode,
|
|
on_download_state_change=on_download_state_change,
|
|
extra_menu_items=[
|
|
(Gtk.ModelButton(text=remove_text), on_remove_songs_click),
|
|
],
|
|
)
|
|
|
|
# Album Art column. This function defines what image to use for the play queue
|
|
# song icon.
|
|
def filename_to_pixbuf(manager: Manager):
|
|
def f2p(
|
|
column: Any,
|
|
cell: Gtk.CellRendererPixbuf,
|
|
model: Gtk.ListStore,
|
|
tree_iter: Gtk.TreeIter,
|
|
flags: Any,
|
|
):
|
|
cell.set_property("sensitive", model.get_value(tree_iter, 0))
|
|
filename = model.get_value(tree_iter, 1)
|
|
playing = model.get_value(tree_iter, 3)
|
|
|
|
pixbuf = manager.get_play_queue_cover_art(filename, playing)
|
|
if not pixbuf:
|
|
cell.set_property("icon_name", "")
|
|
else:
|
|
cell.set_property("pixbuf", pixbuf)
|
|
|
|
return f2p
|
|
|
|
|
|
def create_play_queue_list(manager: Manager):
|
|
view = Gtk.TreeView(
|
|
model=manager.play_queue_store,
|
|
reorderable=True,
|
|
headers_visible=False,
|
|
)
|
|
view.get_style_context().add_class("background")
|
|
|
|
renderer = Gtk.CellRendererPixbuf()
|
|
renderer.set_fixed_size(55, 60)
|
|
column = Gtk.TreeViewColumn("", renderer)
|
|
column.set_cell_data_func(renderer, filename_to_pixbuf(manager))
|
|
column.set_resizable(True)
|
|
view.append_column(column)
|
|
|
|
renderer = Gtk.CellRendererText(markup=True, ellipsize=Pango.EllipsizeMode.END)
|
|
column = Gtk.TreeViewColumn("", renderer, markup=2, sensitive=0)
|
|
column.set_expand(True)
|
|
view.append_column(column)
|
|
|
|
renderer = Gtk.CellRendererPixbuf(icon_name="view-more-symbolic")
|
|
renderer.set_fixed_size(45, 60)
|
|
view_more_column = Gtk.TreeViewColumn("", renderer)
|
|
view_more_column.set_resizable(True)
|
|
view.append_column(view_more_column)
|
|
|
|
renderer = Gtk.CellRendererPixbuf(icon_name="window-close-symbolic")
|
|
renderer.set_fixed_size(45, 60)
|
|
close_column = Gtk.TreeViewColumn("", renderer)
|
|
close_column.set_resizable(True)
|
|
view.append_column(close_column)
|
|
|
|
def on_play_queue_button_press(tree: Any, event: Gdk.EventButton) -> bool:
|
|
if event.button == 1:
|
|
path, column, cell_x, cell_y = tree.get_path_at_pos(event.x, event.y)
|
|
song_idx = path.get_indices()[0]
|
|
|
|
tree_width = tree.get_allocation().width
|
|
|
|
if column == close_column:
|
|
# manager.emit("songs-removed", [song_idx])
|
|
pass
|
|
elif column == view_more_column:
|
|
area = tree.get_cell_area(path, view_more_column)
|
|
x = area.x + area.width / 2
|
|
y = area.y + area.height / 2
|
|
show_play_queue_popover(manager, view, [path], x, y)
|
|
else:
|
|
run_action(view, 'app.play-song', song_idx, [], {"no_reshuffle": None})
|
|
|
|
return True
|
|
return False
|
|
view.connect("button-press-event", on_play_queue_button_press)
|
|
return view
|