Port UI to GTK 4 (close #85)
This commit is contained in:
parent
6ba8bc550f
commit
75b99e5820
36 changed files with 1730 additions and 3285 deletions
|
|
@ -2,56 +2,24 @@
|
|||
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gtk', '4.0')
|
||||
gi.require_version('Adw', '1')
|
||||
import logging
|
||||
import math
|
||||
import threading
|
||||
|
||||
from gi.repository import Gtk, GObject, GdkPixbuf
|
||||
from gi.repository import Gtk, Gdk, Gio, GObject, GdkPixbuf, Adw
|
||||
|
||||
from mcg import client
|
||||
from mcg.albumheaderbar import AlbumHeaderbar
|
||||
from mcg.utils import Utils
|
||||
|
||||
|
||||
|
||||
|
||||
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/playlist-toolbar.ui')
|
||||
class PlaylistToolbar(Gtk.ButtonBox):
|
||||
__gtype_name__ = 'McgPlaylistToolbar'
|
||||
__gsignals__ = {
|
||||
'select': (GObject.SIGNAL_RUN_FIRST, None, (bool,)),
|
||||
'clear-playlist': (GObject.SIGNAL_RUN_FIRST, None, ())
|
||||
}
|
||||
|
||||
# Widgets
|
||||
playlist_clear_button = Gtk.Template.Child()
|
||||
select_button = Gtk.Template.Child()
|
||||
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_select_toggled(self, widget):
|
||||
self.emit('select', widget.get_active())
|
||||
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_clear_clicked(self, widget):
|
||||
if widget is self.playlist_clear_button:
|
||||
self.emit('clear-playlist')
|
||||
|
||||
|
||||
def exit_selection(self):
|
||||
self.select_button.set_active(False)
|
||||
from mcg.utils import GridItem
|
||||
|
||||
|
||||
|
||||
|
||||
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/playlist-panel.ui')
|
||||
class PlaylistPanel(Gtk.Stack):
|
||||
class PlaylistPanel(Adw.Bin):
|
||||
__gtype_name__ = 'McgPlaylistPanel'
|
||||
__gsignals__ = {
|
||||
'open-standalone': (GObject.SIGNAL_RUN_FIRST, None, ()),
|
||||
|
|
@ -60,13 +28,19 @@ class PlaylistPanel(Gtk.Stack):
|
|||
'remove-album': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
|
||||
'remove-multiple-albums': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
|
||||
'play': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
|
||||
'albumart': (GObject.SIGNAL_RUN_FIRST, None, (str,))
|
||||
'albumart': (GObject.SIGNAL_RUN_FIRST, None, (str,)),
|
||||
}
|
||||
|
||||
|
||||
# Widgets
|
||||
playlist_stack = Gtk.Template.Child()
|
||||
panel_normal = Gtk.Template.Child()
|
||||
panel_standalone = Gtk.Template.Child()
|
||||
actionbar_revealer = Gtk.Template.Child()
|
||||
# Toolbar
|
||||
toolbar = Gtk.Template.Child()
|
||||
playlist_clear_button = Gtk.Template.Child()
|
||||
select_button = Gtk.Template.Child()
|
||||
# Playlist Grid
|
||||
playlist_grid = Gtk.Template.Child()
|
||||
# Action bar (normal)
|
||||
|
|
@ -79,8 +53,8 @@ class PlaylistPanel(Gtk.Stack):
|
|||
standalone_image = Gtk.Template.Child()
|
||||
|
||||
|
||||
def __init__(self, client):
|
||||
GObject.GObject.__init__(self)
|
||||
def __init__(self, client, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._client = client
|
||||
self._host = None
|
||||
self._item_size = 150
|
||||
|
|
@ -88,25 +62,21 @@ class PlaylistPanel(Gtk.Stack):
|
|||
self._playlist_albums = None
|
||||
self._playlist_lock = threading.Lock()
|
||||
self._playlist_stop = threading.Event()
|
||||
self._icon_theme = Gtk.IconTheme.get_default()
|
||||
self._icon_theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default())
|
||||
self._standalone_pixbuf = None
|
||||
self._selected_albums = []
|
||||
self._is_selected = False
|
||||
|
||||
# Widgets
|
||||
self._toolbar = PlaylistToolbar()
|
||||
self._toolbar.connect('select', self.on_toolbar_select)
|
||||
self._toolbar.connect('clear-playlist', self.on_toolbar_clear)
|
||||
# Header bar
|
||||
self._headerbar_standalone = AlbumHeaderbar()
|
||||
self._headerbar_standalone.connect('close', self.on_headerbar_close_clicked)
|
||||
# Playlist Grid: Model
|
||||
self._playlist_grid_model = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str)
|
||||
self._playlist_grid_model = Gio.ListStore()
|
||||
self._playlist_grid_selection_multi = Gtk.MultiSelection.new(self._playlist_grid_model)
|
||||
self._playlist_grid_selection_single = Gtk.SingleSelection.new(self._playlist_grid_model)
|
||||
# Playlist Grid
|
||||
self.playlist_grid.set_model(self._playlist_grid_model)
|
||||
self.playlist_grid.set_pixbuf_column(0)
|
||||
self.playlist_grid.set_text_column(-1)
|
||||
self.playlist_grid.set_tooltip_column(1)
|
||||
self.playlist_grid.set_model(self._playlist_grid_selection_single)
|
||||
|
||||
|
||||
def get_headerbar_standalone(self):
|
||||
|
|
@ -114,39 +84,43 @@ class PlaylistPanel(Gtk.Stack):
|
|||
|
||||
|
||||
def get_toolbar(self):
|
||||
return self._toolbar
|
||||
return self.toolbar
|
||||
|
||||
|
||||
def set_selected(self, selected):
|
||||
self._is_selected = selected
|
||||
|
||||
|
||||
def on_toolbar_select(self, widget, active):
|
||||
if active:
|
||||
@Gtk.Template.Callback()
|
||||
def on_select_toggled(self, widget):
|
||||
if self.select_button.get_active():
|
||||
self.actionbar_revealer.set_reveal_child(True)
|
||||
self.playlist_grid.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
|
||||
self.playlist_grid.set_model(self._playlist_grid_selection_multi)
|
||||
self.playlist_grid.set_single_click_activate(False)
|
||||
self.playlist_grid.get_style_context().add_class(Utils.CSS_SELECTION)
|
||||
else:
|
||||
self.actionbar_revealer.set_reveal_child(False)
|
||||
self.playlist_grid.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
||||
self.playlist_grid.set_model(self._playlist_grid_selection_single)
|
||||
self.playlist_grid.set_single_click_activate(True)
|
||||
self.playlist_grid.get_style_context().remove_class(Utils.CSS_SELECTION)
|
||||
|
||||
|
||||
def on_toolbar_clear(self, widget):
|
||||
@Gtk.Template.Callback()
|
||||
def on_clear_clicked(self, widget):
|
||||
self.emit('clear-playlist')
|
||||
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_playlist_grid_clicked(self, widget, path):
|
||||
def on_playlist_grid_clicked(self, widget, position):
|
||||
# Get selected album
|
||||
iter = self._playlist_grid_model.get_iter(path)
|
||||
hash = self._playlist_grid_model.get_value(iter, 2)
|
||||
album = self._playlist_albums[hash]
|
||||
item = self._playlist_grid_model.get_item(position)
|
||||
album = item.get_album()
|
||||
id = album.get_id()
|
||||
self._selected_albums = [album]
|
||||
self.emit('albumart', hash)
|
||||
self.emit('albumart', id)
|
||||
|
||||
# Show standalone album
|
||||
if widget.get_selection_mode() == Gtk.SelectionMode.SINGLE:
|
||||
if widget.get_model() == self._playlist_grid_selection_single:
|
||||
# Set labels
|
||||
self._headerbar_standalone.set_album(album)
|
||||
|
||||
|
|
@ -158,29 +132,15 @@ class PlaylistPanel(Gtk.Stack):
|
|||
self.standalone_spinner.start()
|
||||
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_playlist_grid_selection_changed(self, widget):
|
||||
self._selected_albums = []
|
||||
for path in widget.get_selected_items():
|
||||
iter = self._playlist_grid_model.get_iter(path)
|
||||
hash = self._playlist_grid_model.get_value(iter, 2)
|
||||
self._selected_albums.append(self._playlist_albums[hash])
|
||||
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_selection_cancel_clicked(self, widget):
|
||||
self._toolbar.exit_selection()
|
||||
self.select_button.set_active(False)
|
||||
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_selection_remove_clicked(self, widget):
|
||||
self.emit('remove-multiple-albums', self._selected_albums)
|
||||
self._toolbar.exit_selection()
|
||||
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_standalone_scroll_size_allocate(self, widget, allocation):
|
||||
self._resize_standalone_image()
|
||||
self.emit('remove-multiple-albums', self._get_selected_albums())
|
||||
self.select_button.set_active(False)
|
||||
|
||||
|
||||
def on_headerbar_close_clicked(self, widget):
|
||||
|
|
@ -189,16 +149,20 @@ class PlaylistPanel(Gtk.Stack):
|
|||
|
||||
@Gtk.Template.Callback()
|
||||
def on_standalone_remove_clicked(self, widget):
|
||||
self.emit('remove-album', self._selected_albums[0])
|
||||
self.emit('remove-album', self._get_selected_albums()[0])
|
||||
self._close_standalone()
|
||||
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_standalone_play_clicked(self, widget):
|
||||
self.emit('play', self._selected_albums[0])
|
||||
self.emit('play', self._get_selected_albums()[0])
|
||||
self._close_standalone()
|
||||
|
||||
|
||||
def set_size(self, width, height):
|
||||
self._resize_standalone_image()
|
||||
|
||||
|
||||
def set_item_size(self, item_size):
|
||||
if self._item_size != item_size:
|
||||
self._item_size = item_size
|
||||
|
|
@ -235,23 +199,13 @@ class PlaylistPanel(Gtk.Stack):
|
|||
|
||||
|
||||
def _set_playlist(self, host, playlist, size):
|
||||
if not self._is_selected and self._playlist != playlist:
|
||||
GObject.idle_add(
|
||||
self.get_parent().child_set_property,
|
||||
self,
|
||||
'needs-attention',
|
||||
True
|
||||
)
|
||||
self._playlist_lock.acquire()
|
||||
self._playlist_stop.clear()
|
||||
self._playlist = playlist
|
||||
self._playlist_albums = {}
|
||||
for album in playlist:
|
||||
self._playlist_albums[album.get_id()] = album
|
||||
self.playlist_grid.set_model(None)
|
||||
self.playlist_grid.freeze_child_notify()
|
||||
self._playlist_grid_model.clear()
|
||||
GObject.idle_add(self.playlist_grid.set_item_padding, size / 100)
|
||||
self._playlist_grid_model.remove_all()
|
||||
|
||||
cache = client.MCGCache(host, size)
|
||||
for album in playlist:
|
||||
|
|
@ -265,31 +219,22 @@ class PlaylistPanel(Gtk.Stack):
|
|||
except Exception:
|
||||
self._logger.exception("Failed to load albumart")
|
||||
if pixbuf is None:
|
||||
pixbuf = self._icon_theme.load_icon(
|
||||
pixbuf = self._icon_theme.lookup_icon(
|
||||
Utils.STOCK_ICON_DEFAULT,
|
||||
None,
|
||||
self._item_size,
|
||||
Gtk.IconLookupFlags.FORCE_SVG & Gtk.IconLookupFlags.FORCE_SIZE
|
||||
self._item_size,
|
||||
Gtk.TextDirection.LTR,
|
||||
Gtk.IconLookupFlags.FORCE_SYMBOLIC
|
||||
)
|
||||
if pixbuf is not None:
|
||||
self._playlist_grid_model.append([
|
||||
pixbuf,
|
||||
GObject.markup_escape_text("\n".join([
|
||||
album.get_title(),
|
||||
', '.join(album.get_dates()),
|
||||
Utils.create_artists_label(album),
|
||||
Utils.create_length_label(album)
|
||||
])),
|
||||
album.get_id()
|
||||
])
|
||||
self._playlist_grid_model.append(GridItem(album, pixbuf))
|
||||
|
||||
if self._playlist_stop.is_set():
|
||||
self._playlist_lock.release()
|
||||
return
|
||||
|
||||
self.playlist_grid.set_model(self._playlist_grid_model)
|
||||
self.playlist_grid.thaw_child_notify()
|
||||
# TODO why set_columns()?
|
||||
#self.playlist_grid.set_columns(len(playlist))
|
||||
self.playlist_grid.set_model(self._playlist_grid_selection_single)
|
||||
self._playlist_lock.release()
|
||||
|
||||
|
||||
|
|
@ -305,12 +250,12 @@ class PlaylistPanel(Gtk.Stack):
|
|||
|
||||
|
||||
def _open_standalone(self):
|
||||
self.set_visible_child(self.panel_standalone)
|
||||
self.playlist_stack.set_visible_child(self.panel_standalone)
|
||||
self.emit('open-standalone')
|
||||
|
||||
|
||||
def _close_standalone(self):
|
||||
self.set_visible_child(self.get_children()[0])
|
||||
self.playlist_stack.set_visible_child(self.panel_normal)
|
||||
self.emit('close-standalone')
|
||||
|
||||
|
||||
|
|
@ -318,15 +263,19 @@ class PlaylistPanel(Gtk.Stack):
|
|||
"""Diese Methode skaliert das geladene Bild aus dem Pixelpuffer
|
||||
auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse
|
||||
"""
|
||||
# Get size
|
||||
size_width = self.standalone_stack.get_width()
|
||||
size_height = self.standalone_stack.get_height()
|
||||
|
||||
# Get pixelbuffer
|
||||
pixbuf = self._standalone_pixbuf
|
||||
size = self.standalone_scroll.get_allocation()
|
||||
# Check pixelbuffer
|
||||
if pixbuf is None:
|
||||
return
|
||||
|
||||
# Skalierungswert für Breite und Höhe ermitteln
|
||||
ratioW = float(size.width) / float(pixbuf.get_width())
|
||||
ratioH = float(size.height) / float(pixbuf.get_height())
|
||||
ratioW = float(size_width) / float(pixbuf.get_width())
|
||||
ratioH = float(size_height) / float(pixbuf.get_height())
|
||||
# Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren
|
||||
ratio = min(ratioW, ratioH)
|
||||
ratio = min(ratio, 1)
|
||||
|
|
@ -336,18 +285,24 @@ class PlaylistPanel(Gtk.Stack):
|
|||
if width <= 0 or height <= 0:
|
||||
return
|
||||
# Pixelpuffer auf Oberfläche zeichnen
|
||||
self.standalone_image.set_allocation(self.standalone_scroll.get_allocation())
|
||||
self.standalone_image.set_from_pixbuf(pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER))
|
||||
self.standalone_image.show()
|
||||
|
||||
|
||||
def _get_default_image(self):
|
||||
return self._icon_theme.load_icon(
|
||||
return self._icon_theme.lookup_icon(
|
||||
Utils.STOCK_ICON_DEFAULT,
|
||||
None,
|
||||
512,
|
||||
Gtk.IconLookupFlags.FORCE_SVG & Gtk.IconLookupFlags.FORCE_SIZE
|
||||
512,
|
||||
Gtk.TextDirection.LTR,
|
||||
Gtk.IconLookupFlags.FORCE_SYMBOLIC
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
def _get_selected_albums(self):
|
||||
albums = []
|
||||
for i in range(self.playlist_grid.get_model().get_n_items()):
|
||||
if self.playlist_grid.get_model().is_selected(i):
|
||||
albums.append(self.playlist_grid.get_model().get_item(i).get_album())
|
||||
return albums
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue