made popoveres persistent

This commit is contained in:
Martin Wagner 2021-03-24 17:35:05 +01:00
parent 3d82a8fc5a
commit 2b9475efe7

View File

@ -1417,21 +1417,12 @@ class FocusFrame(Gtk.Overlay):
self._widget.connect("focus-out-event", lambda *args: self._frame.hide()) self._widget.connect("focus-out-event", lambda *args: self._frame.hide())
class SongPopover(Gtk.Popover): class SongPopover(Gtk.Popover):
def __init__(self, uri, client, relative, x, y, offset=26): def __init__(self, client):
super().__init__() super().__init__()
rect=Gdk.Rectangle()
rect.x=x
# Gtk places popovers in treeviews 26px above the given position for no obvious reasons, so I move them 26px
# This seems to be related to the width/height of the headers in treeviews
rect.y=y+offset
rect.width=1
rect.height=1
self.set_pointing_to(rect)
self.set_relative_to(relative)
# client calls # adding vars
song=client.get_metadata(uri) self._client=client
abs_path=client.get_absolute_path(uri) self._rect=Gdk.Rectangle()
# popover css # popover css
style_context=self.get_style_context() style_context=self.get_style_context()
@ -1440,61 +1431,79 @@ class SongPopover(Gtk.Popover):
provider.load_from_data(css) provider.load_from_data(css)
style_context.add_provider(provider, 600) style_context.add_provider(provider, 600)
# open with button # open-with button
open_button=Gtk.Button(image=Gtk.Image.new_from_icon_name("document-open-symbolic",Gtk.IconSize.BUTTON),tooltip_text=_("Open with…")) open_button=Gtk.Button(image=Gtk.Image.new_from_icon_name("document-open-symbolic",Gtk.IconSize.BUTTON),tooltip_text=_("Open with…"))
style_context=open_button.get_style_context() style_context=open_button.get_style_context()
style_context.add_class("circular") style_context.add_class("circular")
open_button_revealer=Gtk.Revealer() # needed for open_button tooltip
open_button_revealer.set_halign(Gtk.Align.END) # button revealer
open_button_revealer.set_valign(Gtk.Align.END) self._open_button_revealer=Gtk.Revealer()
open_button_revealer.add(open_button) self._open_button_revealer.set_halign(Gtk.Align.END)
self._open_button_revealer.set_valign(Gtk.Align.END)
self._open_button_revealer.add(open_button)
# treeview # treeview
# (tag, display-value, tooltip) # (tag, display-value, tooltip)
store=Gtk.ListStore(str, str, str) self._store=Gtk.ListStore(str, str, str)
treeview=Gtk.TreeView(model=store, headers_visible=False, search_column=-1, tooltip_column=2) self._treeview=Gtk.TreeView(model=self._store, headers_visible=False, search_column=-1, tooltip_column=2)
treeview.set_can_focus(False) self._treeview.set_can_focus(False)
treeview.get_selection().set_mode(Gtk.SelectionMode.NONE) self._treeview.get_selection().set_mode(Gtk.SelectionMode.NONE)
# columns # columns
renderer_text=Gtk.CellRendererText(width_chars=50, ellipsize=Pango.EllipsizeMode.MIDDLE, ellipsize_set=True) renderer_text=Gtk.CellRendererText(width_chars=50, ellipsize=Pango.EllipsizeMode.MIDDLE, ellipsize_set=True)
renderer_text_ralign=Gtk.CellRendererText(xalign=1.0, weight=Pango.Weight.BOLD) renderer_text_ralign=Gtk.CellRendererText(xalign=1.0, weight=Pango.Weight.BOLD)
column_tag=Gtk.TreeViewColumn(_("MPD-Tag"), renderer_text_ralign, text=0) column_tag=Gtk.TreeViewColumn(_("MPD-Tag"), renderer_text_ralign, text=0)
column_tag.set_property("resizable", False) column_tag.set_property("resizable", False)
treeview.append_column(column_tag) self._treeview.append_column(column_tag)
column_value=Gtk.TreeViewColumn(_("Value"), renderer_text, text=1) column_value=Gtk.TreeViewColumn(_("Value"), renderer_text, text=1)
column_value.set_property("resizable", False) column_value.set_property("resizable", False)
treeview.append_column(column_value) self._treeview.append_column(column_value)
# populate # scroll
song=ClientHelper.song_to_str_dict(song) self._scroll=Gtk.ScrolledWindow(border_width=3)
for tag, value in song.items(): self._scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
tooltip=value.replace("&", "&") self._scroll.set_propagate_natural_height(True)
if tag == "time": self._scroll.add(self._treeview)
store.append([tag+":", ClientHelper.seconds_to_display_time(int(value)), tooltip])
elif tag == "last-modified": # connect
time=datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ") open_button.connect("clicked", self._on_open_button_clicked)
store.append([tag+":", time.strftime("%a %d %B %Y, %H%M UTC"), tooltip])
else:
store.append([tag+":", value, tooltip])
# packing # packing
overlay=Gtk.Overlay() overlay=Gtk.Overlay()
scroll=Gtk.ScrolledWindow(border_width=3) overlay.add(self._scroll)
scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) overlay.add_overlay(self._open_button_revealer)
window=self.get_toplevel()
scroll.set_max_content_height(window.get_size()[1]//2)
scroll.set_propagate_natural_height(True)
scroll.add(treeview)
overlay.add(scroll)
overlay.add_overlay(open_button_revealer)
if abs_path is not None: # show open with button when song is on the same computer
self._gfile=Gio.File.new_for_path(abs_path)
open_button.connect("clicked", self._on_open_button_clicked)
open_button_revealer.set_reveal_child(True)
self.add(overlay) self.add(overlay)
overlay.show_all() overlay.show_all()
def open(self, uri, widget, x, y, offset=26):
self._rect.x=x
# Gtk places popovers in treeviews 26px above the given position for no obvious reasons, so I move them 26px
# This seems to be related to the width/height of the headers in treeviews
self._rect.y=y+offset
self.set_pointing_to(self._rect)
self.set_relative_to(widget)
window=self.get_toplevel()
self._scroll.set_max_content_height(window.get_size()[1]//2)
self._store.clear()
song=ClientHelper.song_to_str_dict(self._client.get_metadata(uri))
for tag, value in song.items():
tooltip=value.replace("&", "&")
if tag == "time":
self._store.append([tag+":", ClientHelper.seconds_to_display_time(int(value)), tooltip])
elif tag == "last-modified":
time=datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ")
self._store.append([tag+":", time.strftime("%a %d %B %Y, %H%M UTC"), tooltip])
else:
self._store.append([tag+":", value, tooltip])
abs_path=self._client.get_absolute_path(uri)
if abs_path is None: # show open with button when song is on the same computer
self._open_button_revealer.set_reveal_child(False)
else:
self._gfile=Gio.File.new_for_path(abs_path)
self._open_button_revealer.set_reveal_child(True)
self.popup()
self._treeview.columns_autosize()
def _on_open_button_clicked(self, *args): def _on_open_button_clicked(self, *args):
self.popdown() self.popdown()
dialog=Gtk.AppChooserDialog(gfile=self._gfile, transient_for=self.get_toplevel()) dialog=Gtk.AppChooserDialog(gfile=self._gfile, transient_for=self.get_toplevel())
@ -1510,7 +1519,7 @@ class SongsView(Gtk.TreeView):
super().__init__(model=store, search_column=-1) super().__init__(model=store, search_column=-1)
self.columns_autosize() self.columns_autosize()
# add vars # adding vars
self._client=client self._client=client
self._store=store self._store=store
self._file_column_id=file_column_id self._file_column_id=file_column_id
@ -1519,6 +1528,9 @@ class SongsView(Gtk.TreeView):
# selection # selection
self._selection=self.get_selection() self._selection=self.get_selection()
# song popover
self._song_popover=SongPopover(self._client)
# connect # connect
self.connect("row-activated", self._on_row_activated) self.connect("row-activated", self._on_row_activated)
self.connect("button-press-event", self._on_button_press_event) self.connect("button-press-event", self._on_button_press_event)
@ -1558,10 +1570,9 @@ class SongsView(Gtk.TreeView):
elif event.button == 3 and event.type == Gdk.EventType.BUTTON_RELEASE: elif event.button == 3 and event.type == Gdk.EventType.BUTTON_RELEASE:
uri=self._store[path][self._file_column_id] uri=self._store[path][self._file_column_id]
if self.get_property("headers-visible"): if self.get_property("headers-visible"):
pop=SongPopover(uri, self._client, widget, int(event.x), int(event.y)) self._song_popover.open(uri, widget, int(event.x), int(event.y))
else: else:
pop=SongPopover(uri, self._client, widget, int(event.x), int(event.y), offset=0) self._song_popover.open(uri, widget, int(event.x), int(event.y), offset=0)
pop.popup()
self._button_event=(None, None) self._button_event=(None, None)
def _on_key_release_event(self, widget, event): def _on_key_release_event(self, widget, event):
@ -1574,8 +1585,7 @@ class SongsView(Gtk.TreeView):
elif event.keyval == Gdk.keyval_from_name("Menu"): elif event.keyval == Gdk.keyval_from_name("Menu"):
path=self._store.get_path(treeiter) path=self._store.get_path(treeiter)
cell=self.get_cell_area(path, None) cell=self.get_cell_area(path, None)
pop=SongPopover(self._store[path][self._file_column_id], self._client, widget, cell.x, cell.y) self._song_popover.open(self._store[path][self._file_column_id], widget, cell.x, cell.y)
pop.popup()
class SongsWindow(Gtk.Box): class SongsWindow(Gtk.Box):
def __init__(self, client, store, file_column_id, focus_indicator=True): def __init__(self, client, store, file_column_id, focus_indicator=True):
@ -1647,23 +1657,56 @@ class SongsWindow(Gtk.Box):
self._client.files_to_playlist(self._songs_view.get_files(), "enqueue") self._client.files_to_playlist(self._songs_view.get_files(), "enqueue")
class AlbumPopover(Gtk.Popover): class AlbumPopover(Gtk.Popover):
def __init__(self, client, settings, album, album_artist, year, widget, x, y): def __init__(self, client, settings):
super().__init__() super().__init__()
rect=Gdk.Rectangle()
rect.x=x
rect.y=y
rect.width=1
rect.height=1
self.set_pointing_to(rect)
self.set_relative_to(widget)
# adding vars # adding vars
self._client=client self._client=client
songs=self._client.find("album", album, "date", year, settings.get_artist_type(), album_artist) self._settings=settings
self._rect=Gdk.Rectangle()
# store # songs window
# (track, title (artist), duration, file) # (track, title (artist), duration, file)
store=Gtk.ListStore(str, str, str, str) self._store=Gtk.ListStore(str, str, str, str)
songs_window=SongsWindow(self._client, self._store, 3, focus_indicator=False)
# scroll
self._scroll=songs_window.get_scroll()
self._scroll.set_propagate_natural_height(True)
self._scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
self._scroll.set_property("margin-start", 3)
self._scroll.set_property("margin-end", 3)
self._scroll.set_property("margin-top", 3)
# songs view
self._songs_view=songs_window.get_treeview()
self._songs_view.set_property("headers_visible", False)
# columns
renderer_text=Gtk.CellRendererText(width_chars=80, ellipsize=Pango.EllipsizeMode.END, ellipsize_set=True)
renderer_text_ralign=Gtk.CellRendererText(xalign=1.0)
column_track=Gtk.TreeViewColumn(_("No"), renderer_text_ralign, text=0)
column_track.set_property("resizable", False)
self._songs_view.append_column(column_track)
column_title=Gtk.TreeViewColumn(_("Title"), renderer_text, markup=1)
column_title.set_property("resizable", False)
self._songs_view.append_column(column_title)
column_time=Gtk.TreeViewColumn(_("Length"), renderer_text_ralign, text=2)
column_time.set_property("resizable", False)
self._songs_view.append_column(column_time)
# packing
self.add(songs_window)
songs_window.show_all()
def open(self, album, album_artist, date, widget, x, y):
self._rect.x=x
self._rect.y=y
self.set_pointing_to(self._rect)
self.set_relative_to(widget)
self._scroll.set_max_content_height(4*widget.get_allocated_height()//7)
self._store.clear()
songs=self._client.find("album", album, "date", date, self._settings.get_artist_type(), album_artist)
for s in songs: for s in songs:
song=ClientHelper.song_to_list_dict(ClientHelper.pepare_song_for_display(s)) song=ClientHelper.song_to_list_dict(ClientHelper.pepare_song_for_display(s))
track=song["track"][0] track=song["track"][0]
@ -1679,41 +1722,9 @@ class AlbumPopover(Gtk.Popover):
else: else:
title_artist="<b>{}</b> - {}".format(title, artist) title_artist="<b>{}</b> - {}".format(title, artist)
title_artist=title_artist.replace("&", "&amp;") title_artist=title_artist.replace("&", "&amp;")
store.append([track, title_artist, song["human_duration"][0], song["file"][0]]) self._store.append([track, title_artist, song["human_duration"][0], song["file"][0]])
self.popup()
# songs window self._songs_view.columns_autosize()
songs_window=SongsWindow(self._client, store, 3, focus_indicator=False)
# scroll
scroll=songs_window.get_scroll()
scroll.set_max_content_height(4*widget.get_allocated_height()//7)
scroll.set_propagate_natural_height(True)
scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
scroll.set_property("margin-start", 3)
scroll.set_property("margin-end", 3)
scroll.set_property("margin-top", 3)
# songs view
songs_view=songs_window.get_treeview()
songs_view.set_property("headers_visible", False)
# columns
renderer_text=Gtk.CellRendererText(width_chars=80, ellipsize=Pango.EllipsizeMode.END, ellipsize_set=True)
renderer_text_ralign=Gtk.CellRendererText(xalign=1.0)
column_track=Gtk.TreeViewColumn(_("No"), renderer_text_ralign, text=0)
column_track.set_property("resizable", False)
songs_view.append_column(column_track)
column_title=Gtk.TreeViewColumn(_("Title"), renderer_text, markup=1)
column_title.set_property("resizable", False)
column_title.set_property("expand", True)
songs_view.append_column(column_title)
column_time=Gtk.TreeViewColumn(_("Length"), renderer_text_ralign, text=2)
column_time.set_property("resizable", False)
songs_view.append_column(column_time)
# packing
self.add(songs_window)
songs_window.show_all()
class Cover(object): class Cover(object):
def __init__(self, settings, raw_song): def __init__(self, settings, raw_song):
@ -2145,6 +2156,9 @@ class AlbumWindow(FocusFrame):
# progress bar # progress bar
self._progress_bar=Gtk.ProgressBar(no_show_all=True) self._progress_bar=Gtk.ProgressBar(no_show_all=True)
# album popover
self._album_popover=AlbumPopover(self._client, self._settings)
# connect # connect
self._iconview.connect("item-activated", self._on_item_activated) self._iconview.connect("item-activated", self._on_item_activated)
self._iconview.connect("button-press-event", self._on_button_press_event) self._iconview.connect("button-press-event", self._on_button_press_event)
@ -2330,8 +2344,7 @@ class AlbumWindow(FocusFrame):
artist=self._store[path][6] artist=self._store[path][6]
v=self._scroll_vadj.get_value() v=self._scroll_vadj.get_value()
h=self._scroll_hadj.get_value() h=self._scroll_hadj.get_value()
self._popover=AlbumPopover(self._client, self._settings, album, artist, year, widget, event.x-h, event.y-v) self._album_popover.open(album, artist, year, widget, event.x-h, event.y-v)
self._popover.popup()
self._button_event=(None, None) self._button_event=(None, None)
def _on_key_release_event(self, widget, event): def _on_key_release_event(self, widget, event):
@ -2348,8 +2361,7 @@ class AlbumWindow(FocusFrame):
rect=widget.get_cell_rect(paths[0], None)[1] rect=widget.get_cell_rect(paths[0], None)[1]
x=rect.x+rect.width//2 x=rect.x+rect.width//2
y=rect.y+rect.height//2 y=rect.y+rect.height//2
self._popover=AlbumPopover(self._client, self._settings, album, artist, year, widget, x, y) self._album_popover.open(album, artist, year, widget, x, y)
self._popover.popup()
def _on_item_activated(self, widget, path): def _on_item_activated(self, widget, path):
treeiter=self._store.get_iter(path) treeiter=self._store.get_iter(path)
@ -2360,9 +2372,7 @@ class AlbumWindow(FocusFrame):
def _on_disconnected(self, *args): def _on_disconnected(self, *args):
self._iconview.set_sensitive(False) self._iconview.set_sensitive(False)
if self._popover is not None: self._album_popover.popdown()
self._popover.destroy()
self._popover=None
def _on_reconnected(self, *args): def _on_reconnected(self, *args):
self._iconview.set_sensitive(True) self._iconview.set_sensitive(True)
@ -2659,7 +2669,9 @@ class CoverEventBox(Gtk.EventBox):
# adding vars # adding vars
self._client=client self._client=client
self._settings=settings self._settings=settings
self._popover=None
# album popover
self._album_popover=AlbumPopover(self._client, self._settings)
# connect # connect
self._button_press_event=self.connect("button-press-event", self._on_button_press_event) self._button_press_event=self.connect("button-press-event", self._on_button_press_event)
@ -2685,15 +2697,10 @@ class CoverEventBox(Gtk.EventBox):
elif event.button == 2 and event.type == Gdk.EventType.BUTTON_PRESS: elif event.button == 2 and event.type == Gdk.EventType.BUTTON_PRESS:
self._client.album_to_playlist(album, artist, album_year, "append") self._client.album_to_playlist(album, artist, album_year, "append")
elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS: elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
self._popover=AlbumPopover( self._album_popover.open(album, artist, album_year, widget, event.x, event.y)
self._client, self._settings, album, artist, album_year, widget, event.x, event.y
)
self._popover.popup()
def _on_disconnected(self, *args): def _on_disconnected(self, *args):
if self._popover is not None: self._album_popover.popdown()
self._popover.destroy()
self._popover=None
class MainCover(Gtk.Frame): class MainCover(Gtk.Frame):
def __init__(self, client, settings): def __init__(self, client, settings):
@ -2753,7 +2760,6 @@ class PlaylistWindow(Gtk.Box):
self._icon_size=self._settings.get_int("icon-size-sec") self._icon_size=self._settings.get_int("icon-size-sec")
self._inserted_path=None # needed for drag and drop self._inserted_path=None # needed for drag and drop
self._button_event=(None, None) self._button_event=(None, None)
self._popover=None
# buttons # buttons
provider=Gtk.CssProvider() provider=Gtk.CssProvider()
@ -2832,6 +2838,9 @@ class PlaylistWindow(Gtk.Box):
action_bar.pack_end(self._clear_button) action_bar.pack_end(self._clear_button)
action_bar.pack_end(audio) action_bar.pack_end(audio)
# song popover
self._song_popover=SongPopover(self._client)
# connect # connect
self._treeview.connect("row-activated", self._on_row_activated) self._treeview.connect("row-activated", self._on_row_activated)
self._treeview.connect("button-press-event", self._on_button_press_event) self._treeview.connect("button-press-event", self._on_button_press_event)
@ -2922,8 +2931,7 @@ class PlaylistWindow(Gtk.Box):
if event.button == 2 and event.type == Gdk.EventType.BUTTON_RELEASE: if event.button == 2 and event.type == Gdk.EventType.BUTTON_RELEASE:
self._store.remove(self._store.get_iter(path)) self._store.remove(self._store.get_iter(path))
elif event.button == 3 and event.type == Gdk.EventType.BUTTON_RELEASE: elif event.button == 3 and event.type == Gdk.EventType.BUTTON_RELEASE:
self._popover=SongPopover(self._store[path][8], self._client, widget, int(event.x), int(event.y)) self._song_popover.open(self._store[path][8], widget, int(event.x), int(event.y))
self._popover.popup()
self._button_event=(None, None) self._button_event=(None, None)
def _on_key_release_event(self, widget, event): def _on_key_release_event(self, widget, event):
@ -2939,8 +2947,7 @@ class PlaylistWindow(Gtk.Box):
if treeiter is not None: if treeiter is not None:
path=self._store.get_path(treeiter) path=self._store.get_path(treeiter)
cell=self._treeview.get_cell_area(path, None) cell=self._treeview.get_cell_area(path, None)
self._popover=SongPopover(self._store[path][8], self._client, widget, int(cell.x), int(cell.y)) self._song_popover.open(self._store[path][8], widget, int(cell.x), int(cell.y))
self._popover.popup()
def _on_row_deleted(self, model, path): # sync treeview to mpd def _on_row_deleted(self, model, path): # sync treeview to mpd
try: try:
@ -3031,9 +3038,7 @@ class PlaylistWindow(Gtk.Box):
self._treeview.set_sensitive(False) self._treeview.set_sensitive(False)
self._back_to_current_song_button.set_sensitive(False) self._back_to_current_song_button.set_sensitive(False)
self._clear_button.set_sensitive(False) self._clear_button.set_sensitive(False)
if self._popover is not None: self._song_popover.popdown()
self._popover.destroy()
self._popover=None
self._clear() self._clear()
def _on_reconnected(self, *args): def _on_reconnected(self, *args):