removed action bar in playlist window

This commit is contained in:
Martin Wagner 2021-07-16 19:39:48 +02:00
parent 975e0470ae
commit 06f0c0804f

View File

@ -866,12 +866,17 @@ class Client(MPDClient):
else:
self.emitter.emit(key, False)
diff=set(self._last_status)-set(status)
if "songid" in diff:
self.emitter.emit("current_song_changed")
if "volume" in diff:
self.emitter.emit("volume_changed", -1)
if "updating_db" in diff:
self.emitter.emit("update")
for key in diff:
if "songid" == key:
self.emitter.emit("current_song_changed")
elif "volume" == key:
self.emitter.emit("volume_changed", -1)
elif "updating_db" == key:
self.emitter.emit("update")
elif "bitrate" == key:
self.emitter.emit("bitrate", 0.0)
elif "audio" == key:
self.emitter.emit("audio", None)
self._last_status=status
except (MPDBase.ConnectionError, ConnectionResetError) as e:
self.disconnect()
@ -968,7 +973,6 @@ class GeneralSettings(Gtk.Box):
(_("Main cover size:"), (100, 1200, 10), "track-cover"),
(_("Album view cover size:"), (50, 600, 10), "album-cover"),
(_("Action bar icon size:"), (16, 64, 2), "icon-size"),
(_("Secondary icon size:"), (16, 64, 2), "icon-size-sec")
]
for label, (vmin, vmax, step), key in int_settings_data:
int_settings[key]=(Gtk.Label(label=label, xalign=0), Gtk.SpinButton.new_with_range(vmin, vmax, step))
@ -1004,11 +1008,9 @@ class GeneralSettings(Gtk.Box):
view_grid.add(int_settings["track-cover"][0])
view_grid.attach_next_to(int_settings["album-cover"][0], int_settings["track-cover"][0], Gtk.PositionType.BOTTOM, 1, 1)
view_grid.attach_next_to(int_settings["icon-size"][0], int_settings["album-cover"][0], Gtk.PositionType.BOTTOM, 1, 1)
view_grid.attach_next_to(int_settings["icon-size-sec"][0], int_settings["icon-size"][0], Gtk.PositionType.BOTTOM, 1, 1)
view_grid.attach_next_to(int_settings["track-cover"][1], int_settings["track-cover"][0], Gtk.PositionType.RIGHT, 1, 1)
view_grid.attach_next_to(int_settings["album-cover"][1], int_settings["album-cover"][0], Gtk.PositionType.RIGHT, 1, 1)
view_grid.attach_next_to(int_settings["icon-size"][1], int_settings["icon-size"][0], Gtk.PositionType.RIGHT, 1, 1)
view_grid.attach_next_to(int_settings["icon-size-sec"][1], int_settings["icon-size-sec"][0], Gtk.PositionType.RIGHT, 1, 1)
# packing
csd_box=Gtk.Box(spacing=12)
@ -2360,17 +2362,23 @@ class AlbumWindow(FocusFrame):
def display_albums():
for i, album in enumerate(albums):
# tooltip
length_human_readable=ClientHelper.calc_display_length(album["songs"])
titles=len(album["songs"])
duration=ClientHelper.calc_display_length(album["songs"])
length=len(album["songs"])
discs=album["songs"][-1].get("disc", 1)
if type(discs) == list:
discs=int(discs[0])
else:
discs=int(discs)
tooltip=ngettext("{titles} title", "{titles} titles", titles).format(titles=titles)
if discs > 1:
tooltip=" ".join((tooltip, _("on {discs} discs").format(discs=discs)))
tooltip=" ".join((tooltip, "({length})".format(length=length_human_readable)))
tooltip=ngettext(
"{number} song on {discs} discs ({duration})",
"{number} songs on {discs} discs ({duration})",
length
).format(number=length, discs=discs, duration=duration)
else:
tooltip=ngettext(
"{number} song ({duration})", "{number} songs ({duration})", length
).format(number=length, duration=duration)
# album label
if album["year"] == "":
display_label="<b>{}</b>".format(album["album"])
@ -2505,12 +2513,9 @@ class Browser(Gtk.Paned):
# widgets
icons={}
icons_data=("go-previous-symbolic", "system-search-symbolic")
if self._use_csd:
for data in icons_data:
icons[data]=Gtk.Image.new_from_icon_name(data, Gtk.IconSize.BUTTON)
else:
for data in icons_data:
icons[data]=AutoSizedIcon(data, "icon-size-sec", self._settings)
icon_size={True: Gtk.IconSize.BUTTON, False: Gtk.IconSize.LARGE_TOOLBAR}[self._use_csd]
for data in icons_data:
icons[data]=Gtk.Image.new_from_icon_name(data, icon_size)
self.back_to_current_album_button=Gtk.Button(image=icons["go-previous-symbolic"], tooltip_text=_("Back to current album"))
self.back_to_current_album_button.set_can_focus(False)
self.search_button=Gtk.ToggleButton(image=icons["system-search-symbolic"], tooltip_text=_("Search"))
@ -2734,47 +2739,6 @@ class LyricsWindow(FocusFrame):
self._displayed_song_file=None
self._text_buffer.set_text("", -1)
class AudioType(Gtk.Label):
def __init__(self, client):
super().__init__()
self._client=client
self._format, self._brate, self._file_type=("", 0.0, "")
# connect
self._client.emitter.connect("audio", self._on_audio)
self._client.emitter.connect("bitrate", self._on_bitrate)
self._client.emitter.connect("current_song_changed", self._on_song_changed)
self._client.emitter.connect("disconnected", self.clear)
self._client.emitter.connect("state", self._on_state)
def clear(self, *args):
self.set_text("")
self._format, self._brate, self._file_type=("", 0.0, "")
def _refresh(self, *args):
if self._format != "":
string="{} kb/s • {} • {}".format(locale.str(self._brate), ClientHelper.convert_audio_format(self._format), self._file_type)
self.set_text(string)
def _on_audio(self, emitter, audio_format):
self._format=audio_format
self._refresh()
def _on_bitrate(self, emitter, brate):
self._brate=brate
self._refresh()
def _on_song_changed(self, *args):
try:
self._file_type=self._client.currentsong()["file"].split(".")[-1].split("/")[0].upper()
self._refresh()
except:
pass
def _on_state(self, emitter, state):
if state == "stop":
self.clear()
class CoverEventBox(Gtk.EventBox):
def __init__(self, client, settings):
super().__init__()
@ -2847,39 +2811,29 @@ class MainCover(Gtk.Image):
self.set_size_request(size, size)
self._refresh()
class PlaylistWindow(Gtk.Box):
class PlaylistWindow(Gtk.Overlay):
selected_path=GObject.Property(type=Gtk.TreePath, default=None) # currently marked song (bold text)
def __init__(self, client, settings):
super().__init__(orientation=Gtk.Orientation.VERTICAL)
super().__init__()
self._client=client
self._settings=settings
self._playlist_version=None
self._selected_path=None # currently marked song (bold text)
self._icon_size=self._settings.get_int("icon-size-sec")
self._inserted_path=None # needed for drag and drop
# buttons
provider=Gtk.CssProvider()
css=b"""* {min-height: 8px;}""" # allow further shrinking
provider.load_from_data(css)
# back button
self._back_to_current_song_button=Gtk.Button(
image=AutoSizedIcon("go-previous-symbolic", "icon-size-sec", self._settings),
image=Gtk.Image.new_from_icon_name("go-previous-symbolic", Gtk.IconSize.LARGE_TOOLBAR),
tooltip_text=_("Scroll to current song"),
relief=Gtk.ReliefStyle.NONE,
can_focus=False
)
self._back_to_current_song_button.set_margin_bottom(12)
self._back_to_current_song_button.set_margin_start(12)
style_context=self._back_to_current_song_button.get_style_context()
style_context.add_provider(provider, 600)
clear_button=Gtk.Button(
image=AutoSizedIcon("edit-clear-symbolic", "icon-size-sec", self._settings),
tooltip_text=_("Clear playlist"),
relief=Gtk.ReliefStyle.NONE,
action_name="mpd.clear",
can_focus=False
)
style_context=clear_button.get_style_context()
style_context.add_class("destructive-action")
style_context.add_provider(provider, 600)
style_context.add_class("osd")
self._back_button_revealer=Gtk.Revealer(transition_duration=0)
self._back_button_revealer.set_halign(Gtk.Align.START)
self._back_button_revealer.set_valign(Gtk.Align.END)
self._back_button_revealer.add(self._back_to_current_song_button)
# treeview
# (track, disc, title, artist, album, human duration, date, genre, file, weight, duration)
@ -2917,24 +2871,6 @@ class PlaylistWindow(Gtk.Box):
self._frame.set_widget(self._treeview)
self._frame.add(scroll)
# audio infos
audio=AudioType(self._client)
audio.set_xalign(1)
audio.set_ellipsize(Pango.EllipsizeMode.END)
# playlist info
self._playlist_info=Gtk.Label(xalign=0, ellipsize=Pango.EllipsizeMode.END)
# action bar
action_bar=Gtk.ActionBar()
action_bar.pack_start(self._back_to_current_song_button)
self._playlist_info.set_margin_start(3)
action_bar.pack_start(self._playlist_info)
audio.set_margin_end(3)
audio.set_margin_start(12)
action_bar.pack_end(clear_button)
action_bar.pack_end(audio)
# song popover
self._song_popover=SongPopover(self._client, show_buttons=False)
@ -2947,6 +2883,8 @@ class PlaylistWindow(Gtk.Box):
self._row_deleted=self._store.connect("row-deleted", self._on_row_deleted)
self._row_inserted=self._store.connect("row-inserted", self._on_row_inserted)
self._back_to_current_song_button.connect("clicked", self._on_back_to_current_song_button_clicked)
scroll.get_vadjustment().connect("value-changed", self._on_show_hide_back_button)
self.connect("notify::selected-path", self._on_show_hide_back_button)
self._client.emitter.connect("playlist_changed", self._on_playlist_changed)
self._client.emitter.connect("current_song_changed", self._on_song_changed)
@ -2959,8 +2897,8 @@ class PlaylistWindow(Gtk.Box):
self._settings.connect("changed::column-permutation", self._load_settings)
# packing
self.pack_start(self._frame, True, True, 0)
self.pack_end(action_bar, False, False, 0)
self.add(self._frame)
self.add_overlay(self._back_button_revealer)
def _on_column_width(self, obj, typestring, pos):
self._settings.array_modify("ai", "column-sizes", pos, obj.get_property("fixed-width"))
@ -2979,9 +2917,9 @@ class PlaylistWindow(Gtk.Box):
def _clear(self, *args):
self._song_popover.popdown()
self._playlist_info.set_text("")
self._set_playlist_info("")
self._playlist_version=None
self._selected_path=None
self.set_property("selected-path", None)
self._store.handler_block(self._row_inserted)
self._store.handler_block(self._row_deleted)
self._store.clear()
@ -2992,17 +2930,17 @@ class PlaylistWindow(Gtk.Box):
self._unselect()
try:
self._store[path][9]=Pango.Weight.BOLD
self._selected_path=path
self.set_property("selected-path", path)
except IndexError: # invalid path
pass
def _unselect(self):
if self._selected_path is not None:
if self.get_property("selected-path") is not None:
try:
self._store[self._selected_path][9]=Pango.Weight.BOOK
self._selected_path=None
self._store[self.get_property("selected-path")][9]=Pango.Weight.BOOK
self.set_property("selected-path", None)
except IndexError: # invalid path
self._selected_path=None
self.set_property("selected-path", None)
def _scroll_to_selected_title(self, *args):
treeview, treeiter=self._selection.get_selected()
@ -3021,6 +2959,12 @@ class PlaylistWindow(Gtk.Box):
self._selection.select_path(path)
self._select(path)
def _set_playlist_info(self, text):
if text == "":
self._columns[2].set_title(_("Title"))
else:
self._columns[2].set_title(" • ".join([_("Title"), text]))
def _on_button_press_event(self, widget, event):
path_re=widget.get_path_at_pos(int(event.x), int(event.y))
if path_re is not None:
@ -3030,6 +2974,14 @@ class PlaylistWindow(Gtk.Box):
elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
self._song_popover.open(self._store[path][8], widget, int(event.x), int(event.y))
def _on_show_hide_back_button(self, *args):
visible_range=self._treeview.get_visible_range()
if visible_range is None or self.get_property("selected-path") is None:
self._back_button_revealer.set_reveal_child(False)
else:
current_song_visible=(visible_range[0] <= self.get_property("selected-path") <= visible_range[1])
self._back_button_revealer.set_reveal_child(not(current_song_visible))
def _on_key_release_event(self, widget, event):
if event.keyval == Gdk.keyval_from_name("Delete"):
treeview, treeiter=self._selection.get_selected()
@ -3075,7 +3027,7 @@ class PlaylistWindow(Gtk.Box):
songs=self._client.playlistinfo()
if songs != []:
self._treeview.freeze_child_notify()
self._playlist_info.set_text("")
self._set_playlist_info("")
for s in songs:
song=ClientHelper.song_to_str_dict(ClientHelper.pepare_song_for_display(s))
try:
@ -3108,11 +3060,11 @@ class PlaylistWindow(Gtk.Box):
self._store.remove(treeiter)
playlist_length=len(self._store)
if playlist_length == 0:
self._playlist_info.set_text("")
self._set_playlist_info("")
else:
length_human_readable=ClientHelper.seconds_to_display_time(int(sum([row[10] for row in self._store])))
titles=ngettext("{titles} title", "{titles} titles", playlist_length).format(titles=playlist_length)
self._playlist_info.set_text(" ".join((titles, "({length})".format(length=length_human_readable))))
duration_human_readable=ClientHelper.seconds_to_display_time(int(sum([row[10] for row in self._store])))
translated_string=ngettext("{number} song ({duration})", "{number} songs ({duration})", playlist_length)
self._set_playlist_info(translated_string.format(number=playlist_length, duration=duration_human_readable))
self._refresh_selection()
if self._playlist_version != version:
self._scroll_to_selected_title()
@ -3127,8 +3079,8 @@ class PlaylistWindow(Gtk.Box):
def _on_back_to_current_song_button_clicked(self, *args):
self._treeview.set_cursor(Gtk.TreePath(len(self._store)), None, False) # set to invalid TreePath (needed to unset cursor)
if self._selected_path is not None:
self._selection.select_path(self._selected_path)
if self.get_property("selected-path") is not None:
self._selection.select_path(self.get_property("selected-path"))
self._scroll_to_selected_title()
def _on_disconnected(self, *args):
@ -3231,9 +3183,9 @@ class CoverPlaylistWindow(Gtk.Paned):
else:
self._lyrics_button_revealer.set_reveal_child(False)
###################
# control widgets #
###################
######################
# action bar widgets #
######################
class PlaybackControl(Gtk.ButtonBox):
def __init__(self, client, settings):
@ -3422,82 +3374,71 @@ class SeekBar(Gtk.Box):
if state == "stop":
self._disable()
class OutputPopover(Gtk.Popover):
def __init__(self, client, relative):
super().__init__()
self.set_relative_to(relative)
self._client=client
# widgets
box=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, border_width=9)
for output in self._client.outputs():
button=Gtk.ModelButton(label="{} ({})".format(output["outputname"], output["plugin"]), role=Gtk.ButtonRole.CHECK)
button.get_child().set_property("xalign", 0)
if output["outputenabled"] == "1":
button.set_property("active", True)
button.connect("clicked", self._on_button_clicked, output["outputid"])
box.pack_start(button, False, False, 0)
#connect
self.connect("closed", lambda *args: self.destroy())
# packing
self.add(box)
box.show_all()
def _on_button_clicked(self, button, out_id):
if button.get_property("active"):
self._client.disableoutput(out_id)
button.set_property("active", False)
else:
self._client.enableoutput(out_id)
button.set_property("active", True)
class VolumeButton(Gtk.VolumeButton):
class AudioType(Gtk.Box):
def __init__(self, client, settings):
super().__init__(use_symbolic=True, size=settings.get_gtk_icon_size("icon-size"), can_focus=False)
super().__init__(spacing=6)
self._client=client
self._popover=None
self._adj=self.get_adjustment()
self._adj.set_step_increment(0.05)
self._adj.set_page_increment(0.1)
self._adj.set_upper(0) # do not allow volume change by user when MPD has not yet reported volume (no output enabled/avail)
settings.bind("icon-size", self.get_child(), "pixel-size", Gio.SettingsBindFlags.GET)
self._settings=settings
self._top_label=Gtk.Label(xalign=1)
self._brate_label=Gtk.Label(xalign=1, width_chars=5)
self._format_label=Gtk.Label()
# connect
self._changed=self.connect("value-changed", self._set_volume)
self._client.emitter.connect("volume_changed", self._refresh)
self._settings.connect("notify::mini-player", self._on_mini_player)
# self._settings.connect("changed::show-stop", self._on_show_audio_details_changed) # TODO
self._client.emitter.connect("audio", self._on_audio)
self._client.emitter.connect("bitrate", self._on_bitrate)
self._client.emitter.connect("current_song_changed", self._on_song_changed)
self._client.emitter.connect("disconnected", self._on_disconnected)
self._client.emitter.connect("reconnected", self._on_reconnected)
self.connect("button-press-event", self._on_button_press_event)
def _set_volume(self, widget, value):
self._client.setvol(str(int(value*100)))
# packing
hbox=Gtk.Box(halign=Gtk.Align.END)
hbox.pack_start(self._brate_label, False, False, 0)
hbox.pack_start(self._top_label, False, False, 0)
vbox=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, valign=Gtk.Align.CENTER)
vbox.pack_start(hbox, False, False, 0)
vbox.pack_start(self._format_label, False, False, 0)
self.pack_start(Gtk.Separator(), False, False, 0)
self.pack_start(vbox, False, False, 0)
self.pack_start(Gtk.Separator(), False, False, 0)
def _refresh(self, emitter, volume):
self.handler_block(self._changed)
if volume < 0:
self.set_value(0)
self._adj.set_upper(0)
def _on_audio(self, emitter, audio_format):
if audio_format is None:
self._format_label.set_markup("<small> </small>")
else:
self._adj.set_upper(1)
self.set_value(volume/100)
self.handler_unblock(self._changed)
self._format_label.set_markup("<small>"+ClientHelper.convert_audio_format(audio_format)+"</small>")
def _on_button_press_event(self, widget, event):
if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
self._popover=OutputPopover(self._client, self)
self._popover.popup()
def _on_bitrate(self, emitter, brate):
self._brate_label.set_text(locale.str(brate))
def _on_reconnected(self, *args):
self.set_sensitive(True)
def _on_song_changed(self, *args):
current_song=self._client.currentsong()
if current_song == {}:
self._top_label.set_text(" kb/s")
self._format_label.set_markup("<small> </small>")
else:
file_type=current_song["file"].split(".")[-1].split("/")[0].upper()
self._top_label.set_text(" kb/s • "+file_type)
def _on_mini_player(self, obj, typestring):
self._on_show_audio_details_changed()
def _on_show_audio_details_changed(self, *args):
# visibility=(self._settings.get_boolean("show-stop") and not self._settings.get_property("mini-player"))
visibility=not self._settings.get_property("mini-player")
self.set_property("visible", visibility)
self.set_property("no-show-all", not(visibility))
def _on_disconnected(self, *args):
self.set_sensitive(False)
self._refresh(None, -1)
if self._popover is not None:
self._popover.popdown()
self._popover=None
self._top_label.set_width_chars(0)
self._top_label.set_text(_(" kb/s"))
self._brate_label.set_text(locale.str(0.0))
self._format_label.set_markup("<small> </small>")
def _on_reconnected(self, *args):
self.set_sensitive(True)
class PlaybackOptions(Gtk.ButtonBox):
def __init__(self, client, settings):
@ -3578,6 +3519,83 @@ class PlaybackOptions(Gtk.ButtonBox):
self.set_property("no-show-all", False)
self.show_all()
class OutputPopover(Gtk.Popover):
def __init__(self, client, relative):
super().__init__()
self.set_relative_to(relative)
self._client=client
# widgets
box=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, border_width=9)
for output in self._client.outputs():
button=Gtk.ModelButton(label="{} ({})".format(output["outputname"], output["plugin"]), role=Gtk.ButtonRole.CHECK)
button.get_child().set_property("xalign", 0)
if output["outputenabled"] == "1":
button.set_property("active", True)
button.connect("clicked", self._on_button_clicked, output["outputid"])
box.pack_start(button, False, False, 0)
#connect
self.connect("closed", lambda *args: self.destroy())
# packing
self.add(box)
box.show_all()
def _on_button_clicked(self, button, out_id):
if button.get_property("active"):
self._client.disableoutput(out_id)
button.set_property("active", False)
else:
self._client.enableoutput(out_id)
button.set_property("active", True)
class VolumeButton(Gtk.VolumeButton):
def __init__(self, client, settings):
super().__init__(use_symbolic=True, size=settings.get_gtk_icon_size("icon-size"), can_focus=False)
self._client=client
self._popover=None
self._adj=self.get_adjustment()
self._adj.set_step_increment(0.05)
self._adj.set_page_increment(0.1)
self._adj.set_upper(0) # do not allow volume change by user when MPD has not yet reported volume (no output enabled/avail)
settings.bind("icon-size", self.get_child(), "pixel-size", Gio.SettingsBindFlags.GET)
# connect
self._changed=self.connect("value-changed", self._set_volume)
self._client.emitter.connect("volume_changed", self._refresh)
self._client.emitter.connect("disconnected", self._on_disconnected)
self._client.emitter.connect("reconnected", self._on_reconnected)
self.connect("button-press-event", self._on_button_press_event)
def _set_volume(self, widget, value):
self._client.setvol(str(int(value*100)))
def _refresh(self, emitter, volume):
self.handler_block(self._changed)
if volume < 0:
self.set_value(0)
self._adj.set_upper(0)
else:
self._adj.set_upper(1)
self.set_value(volume/100)
self.handler_unblock(self._changed)
def _on_button_press_event(self, widget, event):
if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
self._popover=OutputPopover(self._client, self)
self._popover.popup()
def _on_reconnected(self, *args):
self.set_sensitive(True)
def _on_disconnected(self, *args):
self.set_sensitive(False)
self._refresh(None, -1)
if self._popover is not None:
self._popover.popdown()
self._popover=None
###################
# MPD gio actions #
###################
@ -3804,6 +3822,7 @@ class MainWindow(Gtk.ApplicationWindow):
self._cover_playlist_window=CoverPlaylistWindow(self._client, self._settings)
playback_control=PlaybackControl(self._client, self._settings)
seek_bar=SeekBar(self._client)
audio=AudioType(self._client, self._settings)
playback_options=PlaybackOptions(self._client, self._settings)
volume_button=VolumeButton(self._client, self._settings)
connection_notify=ConnectionNotify(self._client, self._settings)
@ -3836,6 +3855,7 @@ class MainWindow(Gtk.ApplicationWindow):
action_bar=Gtk.ActionBar()
action_bar.pack_start(playback_control)
action_bar.pack_start(seek_bar)
action_bar.pack_start(audio)
action_bar.pack_start(playback_options)
action_bar.pack_start(volume_button)