mirror of
https://github.com/SoongNoonien/mpdevil.git
synced 2023-08-10 21:12:44 +03:00
replaced popovers by menus
This commit is contained in:
parent
0526ce3328
commit
0645816972
161
src/mpdevil.py
161
src/mpdevil.py
@ -868,6 +868,14 @@ class Client(MPDClient):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def show_in_file_manager(self, uri):
|
||||||
|
if (path:=self.get_absolute_path(uri)) is not None:
|
||||||
|
file=Gio.File.new_for_path(path)
|
||||||
|
bus=Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
||||||
|
proxy=Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None, "org.freedesktop.FileManager1",
|
||||||
|
"/org/freedesktop/FileManager1", "org.freedesktop.FileManager1", None)
|
||||||
|
proxy.call_sync("ShowItems", GLib.Variant("(ass)", ((file.get_uri(),),"")), Gio.DBusCallFlags.NONE, 500, None)
|
||||||
|
|
||||||
def toggle_play(self):
|
def toggle_play(self):
|
||||||
status=self.status()
|
status=self.status()
|
||||||
if status["state"] == "play":
|
if status["state"] == "play":
|
||||||
@ -1273,90 +1281,40 @@ class AutoSizedIcon(Gtk.Image):
|
|||||||
super().__init__(icon_name=icon_name)
|
super().__init__(icon_name=icon_name)
|
||||||
settings.bind(settings_key, self, "pixel-size", Gio.SettingsBindFlags.GET)
|
settings.bind(settings_key, self, "pixel-size", Gio.SettingsBindFlags.GET)
|
||||||
|
|
||||||
class SongPopover(Gtk.Popover):
|
class SongsListMenu(Gtk.PopoverMenu):
|
||||||
def __init__(self, client, show_buttons=True):
|
def __init__(self, client):
|
||||||
super().__init__(position=Gtk.PositionType.BOTTOM)
|
super().__init__(position=Gtk.PositionType.BOTTOM)
|
||||||
self._client=client
|
self._client=client
|
||||||
self._rect=Gdk.Rectangle()
|
self._rect=Gdk.Rectangle()
|
||||||
self._uri=None
|
self._uri=None
|
||||||
|
|
||||||
# buttons
|
# buttons
|
||||||
hbox=Gtk.Box(spacing=6)
|
append_button=Gtk.ModelButton(text=_("Append"))
|
||||||
self._open_button=Gtk.Button(image=Gtk.Image.new_from_icon_name("folder-open-symbolic", Gtk.IconSize.BUTTON),
|
play_button=Gtk.ModelButton(text=_("Play"))
|
||||||
tooltip_text=_("Show in file manager"))
|
self._show_button=Gtk.ModelButton(text=_("Show in file manager"))
|
||||||
hbox.pack_end(self._open_button, False, False, 0)
|
|
||||||
if show_buttons:
|
|
||||||
button_box=Gtk.ButtonBox(layout_style=Gtk.ButtonBoxStyle.EXPAND)
|
|
||||||
data=((_("Append"), "list-add-symbolic", "append"), (_("Play"), "media-playback-start-symbolic", "play"))
|
|
||||||
for tooltip, icon, mode in data:
|
|
||||||
button=Gtk.Button(tooltip_text=tooltip, image=Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON))
|
|
||||||
button.connect("clicked", self._on_button_clicked, mode)
|
|
||||||
button_box.pack_start(button, True, True, 0)
|
|
||||||
hbox.pack_end(button_box, False, False, 0)
|
|
||||||
|
|
||||||
# treeview
|
|
||||||
# (tag, display-value, tooltip)
|
|
||||||
self._store=Gtk.ListStore(str, str, str)
|
|
||||||
self._treeview=Gtk.TreeView(model=self._store, headers_visible=False, search_column=-1, tooltip_column=2, can_focus=False)
|
|
||||||
self._treeview.get_selection().set_mode(Gtk.SelectionMode.NONE)
|
|
||||||
|
|
||||||
# columns
|
|
||||||
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)
|
|
||||||
column_tag=Gtk.TreeViewColumn(_("MPD-Tag"), renderer_text_ralign, text=0)
|
|
||||||
column_tag.set_property("resizable", False)
|
|
||||||
self._treeview.append_column(column_tag)
|
|
||||||
column_value=Gtk.TreeViewColumn(_("Value"), renderer_text, text=1)
|
|
||||||
column_value.set_property("resizable", False)
|
|
||||||
self._treeview.append_column(column_value)
|
|
||||||
|
|
||||||
# scroll
|
|
||||||
self._scroll=Gtk.ScrolledWindow(child=self._treeview, propagate_natural_height=True)
|
|
||||||
self._scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
|
||||||
|
|
||||||
# connect
|
# connect
|
||||||
self._open_button.connect("clicked", self._on_open_button_clicked)
|
append_button.connect("clicked", lambda *args: self._client.files_to_playlist([self._uri], "append"))
|
||||||
|
play_button.connect("clicked", lambda *args: self._client.files_to_playlist([self._uri], "play"))
|
||||||
|
self._show_button.connect("clicked", lambda *args: self._client.show_in_file_manager(self._uri))
|
||||||
|
|
||||||
# packing
|
# packing
|
||||||
frame=Gtk.Frame(child=self._scroll)
|
box=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, margin=10)
|
||||||
vbox=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, border_width=6, spacing=6)
|
box.pack_start(append_button, False, False, 0)
|
||||||
vbox.pack_start(frame, True, True, 0)
|
box.pack_start(play_button, False, False, 0)
|
||||||
vbox.pack_end(hbox, False, False, 0)
|
box.pack_start(Gtk.Separator(), False, False, 0)
|
||||||
self.add(vbox)
|
box.pack_start(self._show_button, False, False, 0)
|
||||||
vbox.show_all()
|
self.add(box)
|
||||||
|
box.show_all()
|
||||||
|
|
||||||
def open(self, uri, widget, x, y):
|
def open(self, uri, widget, x, y):
|
||||||
self._uri=uri
|
self._uri=uri
|
||||||
self._rect.x,self._rect.y=x,y
|
self._rect.x,self._rect.y=x,y
|
||||||
self.set_pointing_to(self._rect)
|
self.set_pointing_to(self._rect)
|
||||||
self.set_relative_to(widget)
|
self.set_relative_to(widget)
|
||||||
window=self.get_toplevel()
|
|
||||||
self._scroll.set_max_content_height(window.get_size()[1]//2)
|
|
||||||
self._store.clear()
|
|
||||||
song=self._client.lsinfo(uri)[0]
|
|
||||||
for tag, value in song.items():
|
|
||||||
if tag == "duration":
|
|
||||||
self._store.append([tag+":", str(value), locale.str(value)])
|
|
||||||
elif tag in ("last-modified", "format"):
|
|
||||||
self._store.append([tag+":", str(value), value.raw()])
|
|
||||||
else:
|
|
||||||
self._store.append([tag+":", str(value), GLib.markup_escape_text(str(value))])
|
|
||||||
abs_path=self._client.get_absolute_path(uri)
|
abs_path=self._client.get_absolute_path(uri)
|
||||||
self._open_button.set_sensitive(abs_path is not None) # show open with button when song is on the same computer
|
self._show_button.set_sensitive(abs_path is not None) # show button when song is on the same computer
|
||||||
self.popup()
|
self.popup()
|
||||||
self._treeview.columns_autosize()
|
|
||||||
|
|
||||||
def _on_open_button_clicked(self, *args):
|
|
||||||
self.popdown()
|
|
||||||
file=Gio.File.new_for_path(self._client.get_absolute_path(self._uri))
|
|
||||||
bus=Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
|
||||||
proxy=Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None, "org.freedesktop.FileManager1",
|
|
||||||
"/org/freedesktop/FileManager1", "org.freedesktop.FileManager1", None)
|
|
||||||
proxy.call_sync("ShowItems", GLib.Variant("(ass)", ((file.get_uri(),),"")), Gio.DBusCallFlags.NONE, 500, None)
|
|
||||||
|
|
||||||
def _on_button_clicked(self, widget, mode):
|
|
||||||
self._client.files_to_playlist([self._uri], mode)
|
|
||||||
self.popdown()
|
|
||||||
|
|
||||||
class SongsList(TreeView):
|
class SongsList(TreeView):
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
@ -1400,15 +1358,15 @@ class SongsList(TreeView):
|
|||||||
button.connect("clicked", self._on_button_clicked, mode)
|
button.connect("clicked", self._on_button_clicked, mode)
|
||||||
self.buttons.pack_start(button, True, True, 0)
|
self.buttons.pack_start(button, True, True, 0)
|
||||||
|
|
||||||
# song popover
|
# menu
|
||||||
self._song_popover=SongPopover(self._client)
|
self._menu=SongsListMenu(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)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self._song_popover.popdown()
|
self._menu.popdown()
|
||||||
self._store.clear()
|
self._store.clear()
|
||||||
|
|
||||||
def append(self, track, title, duration, file, search_string=""):
|
def append(self, track, title, duration, file, search_string=""):
|
||||||
@ -1427,14 +1385,14 @@ class SongsList(TreeView):
|
|||||||
elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
|
elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
|
||||||
uri=self._store[path][3]
|
uri=self._store[path][3]
|
||||||
point=self.convert_bin_window_to_widget_coords(event.x,event.y)
|
point=self.convert_bin_window_to_widget_coords(event.x,event.y)
|
||||||
self._song_popover.open(uri, widget, *point)
|
self._menu.open(uri, widget, *point)
|
||||||
|
|
||||||
def _on_button_clicked(self, widget, mode):
|
def _on_button_clicked(self, widget, mode):
|
||||||
self._client.files_to_playlist((row[3] for row in self._store), mode)
|
self._client.files_to_playlist((row[3] for row in self._store), mode)
|
||||||
|
|
||||||
def show_info(self):
|
def show_info(self):
|
||||||
if (path:=self.get_cursor()[0]) is not None:
|
if (path:=self.get_cursor()[0]) is not None:
|
||||||
self._song_popover.open(self._store[path][3], self, *self.get_popover_point(path))
|
self._menu.open(self._store[path][3], self, *self.get_popover_point(path))
|
||||||
|
|
||||||
def add_to_playlist(self, mode):
|
def add_to_playlist(self, mode):
|
||||||
if (path:=self.get_cursor()[0]) is not None:
|
if (path:=self.get_cursor()[0]) is not None:
|
||||||
@ -2191,6 +2149,42 @@ class Browser(Gtk.Paned):
|
|||||||
# playlist #
|
# playlist #
|
||||||
############
|
############
|
||||||
|
|
||||||
|
class PlaylistViewMenu(Gtk.PopoverMenu):
|
||||||
|
__gsignals__={"delete": (GObject.SignalFlags.RUN_FIRST, None, ())}
|
||||||
|
def __init__(self, client):
|
||||||
|
super().__init__(position=Gtk.PositionType.BOTTOM)
|
||||||
|
self._client=client
|
||||||
|
self._rect=Gdk.Rectangle()
|
||||||
|
self._uri=None
|
||||||
|
|
||||||
|
# buttons
|
||||||
|
delete_button=Gtk.ModelButton(text=_("Remove song"), action_name="menu.remove")
|
||||||
|
clean_playlist_button=Gtk.ModelButton(text=_("Clean playlist"), action_name="mpd.clean")
|
||||||
|
clear_playlist_button=Gtk.ModelButton(text=_("Clear playlist"), action_name="mpd.clear")
|
||||||
|
self._show_button=Gtk.ModelButton(text=_("Show in file manager"))
|
||||||
|
|
||||||
|
# connect
|
||||||
|
self._show_button.connect("clicked", lambda *args: self._client.show_in_file_manager(self._uri))
|
||||||
|
|
||||||
|
# packing
|
||||||
|
box=Gtk.Box(orientation=Gtk.Orientation.VERTICAL, margin=10)
|
||||||
|
box.pack_start(delete_button, False, False, 0)
|
||||||
|
box.pack_start(clean_playlist_button, False, False, 0)
|
||||||
|
box.pack_start(clear_playlist_button, False, False, 0)
|
||||||
|
box.pack_start(Gtk.Separator(), False, False, 0)
|
||||||
|
box.pack_start(self._show_button, False, False, 0)
|
||||||
|
self.add(box)
|
||||||
|
box.show_all()
|
||||||
|
|
||||||
|
def open(self, uri, widget, x, y):
|
||||||
|
self._uri=uri
|
||||||
|
self._rect.x,self._rect.y=x,y
|
||||||
|
self.set_pointing_to(self._rect)
|
||||||
|
self.set_relative_to(widget)
|
||||||
|
abs_path=self._client.get_absolute_path(uri)
|
||||||
|
self._show_button.set_sensitive(abs_path is not None) # show button when song is on the same computer
|
||||||
|
self.popup()
|
||||||
|
|
||||||
class PlaylistView(TreeView):
|
class PlaylistView(TreeView):
|
||||||
selected_path=GObject.Property(type=Gtk.TreePath, default=None) # currently marked song
|
selected_path=GObject.Property(type=Gtk.TreePath, default=None) # currently marked song
|
||||||
def __init__(self, client, settings):
|
def __init__(self, client, settings):
|
||||||
@ -2227,8 +2221,13 @@ class PlaylistView(TreeView):
|
|||||||
self._column_title=columns[1]
|
self._column_title=columns[1]
|
||||||
self._column_title.set_property("expand", True)
|
self._column_title.set_property("expand", True)
|
||||||
|
|
||||||
# song popover
|
# menu
|
||||||
self._song_popover=SongPopover(self._client, show_buttons=False)
|
action_group=Gio.SimpleActionGroup()
|
||||||
|
action=Gio.SimpleAction.new("remove", None)
|
||||||
|
action.connect("activate", lambda *args: self._store.remove(self._store.get_iter(self.get_cursor()[0])))
|
||||||
|
action_group.add_action(action)
|
||||||
|
self.insert_action_group("menu", action_group)
|
||||||
|
self._menu=PlaylistViewMenu(self._client)
|
||||||
|
|
||||||
# connect
|
# connect
|
||||||
self.connect("row-activated", self._on_row_activated)
|
self.connect("row-activated", self._on_row_activated)
|
||||||
@ -2243,7 +2242,7 @@ class PlaylistView(TreeView):
|
|||||||
self._client.emitter.connect("connected", self._on_connected)
|
self._client.emitter.connect("connected", self._on_connected)
|
||||||
|
|
||||||
def _clear(self, *args):
|
def _clear(self, *args):
|
||||||
self._song_popover.popdown()
|
self._menu.popdown()
|
||||||
self._playlist_version=None
|
self._playlist_version=None
|
||||||
self.set_property("selected-path", None)
|
self.set_property("selected-path", None)
|
||||||
self._store.handler_block(self._row_inserted)
|
self._store.handler_block(self._row_inserted)
|
||||||
@ -2282,7 +2281,8 @@ class PlaylistView(TreeView):
|
|||||||
self._scroll_to_path(path)
|
self._scroll_to_path(path)
|
||||||
|
|
||||||
def _refresh_selection(self): # Gtk.TreePath(len(self._store) is used to generate an invalid TreePath (needed to unset cursor)
|
def _refresh_selection(self): # Gtk.TreePath(len(self._store) is used to generate an invalid TreePath (needed to unset cursor)
|
||||||
self.set_cursor(Gtk.TreePath(len(self._store)), None, False)
|
if not self._menu.get_visible():
|
||||||
|
self.set_cursor(Gtk.TreePath(len(self._store)), None, False)
|
||||||
song=self._client.status().get("song")
|
song=self._client.status().get("song")
|
||||||
if song is None:
|
if song is None:
|
||||||
self._unselect()
|
self._unselect()
|
||||||
@ -2297,7 +2297,7 @@ class PlaylistView(TreeView):
|
|||||||
self._delete(path)
|
self._delete(path)
|
||||||
elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
|
elif event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS:
|
||||||
point=self.convert_bin_window_to_widget_coords(event.x,event.y)
|
point=self.convert_bin_window_to_widget_coords(event.x,event.y)
|
||||||
self._song_popover.open(self._store[path][3], widget, *point)
|
self._menu.open(self._store[path][3], widget, *point)
|
||||||
|
|
||||||
def _on_key_release_event(self, widget, event):
|
def _on_key_release_event(self, widget, event):
|
||||||
if event.keyval == Gdk.keyval_from_name("Delete"):
|
if event.keyval == Gdk.keyval_from_name("Delete"):
|
||||||
@ -2334,7 +2334,7 @@ class PlaylistView(TreeView):
|
|||||||
def _on_playlist_changed(self, emitter, version):
|
def _on_playlist_changed(self, emitter, version):
|
||||||
self._store.handler_block(self._row_inserted)
|
self._store.handler_block(self._row_inserted)
|
||||||
self._store.handler_block(self._row_deleted)
|
self._store.handler_block(self._row_deleted)
|
||||||
self._song_popover.popdown()
|
self._menu.popdown()
|
||||||
self._unselect()
|
self._unselect()
|
||||||
self._client.restrict_tagtypes("track", "title", "artist", "album", "date")
|
self._client.restrict_tagtypes("track", "title", "artist", "album", "date")
|
||||||
songs=[]
|
songs=[]
|
||||||
@ -2388,7 +2388,7 @@ class PlaylistView(TreeView):
|
|||||||
|
|
||||||
def show_info(self):
|
def show_info(self):
|
||||||
if (path:=self.get_cursor()[0]) is not None:
|
if (path:=self.get_cursor()[0]) is not None:
|
||||||
self._song_popover.open(self._store[path][3], self, *self.get_popover_point(path))
|
self._menu.open(self._store[path][3], self, *self.get_popover_point(path))
|
||||||
|
|
||||||
class PlaylistWindow(Gtk.Overlay):
|
class PlaylistWindow(Gtk.Overlay):
|
||||||
def __init__(self, client, settings):
|
def __init__(self, client, settings):
|
||||||
@ -3032,7 +3032,7 @@ class MPDActionGroup(Gio.SimpleActionGroup):
|
|||||||
self._client=client
|
self._client=client
|
||||||
|
|
||||||
# actions
|
# actions
|
||||||
self._disable_on_stop_data=("next","prev","seek-forward","seek-backward")
|
self._disable_on_stop_data=("next","prev","seek-forward","seek-backward", "clean")
|
||||||
self._enable_on_reconnect_data=("toggle-play","stop","clear","update","repeat","random","single","consume","single-oneshot")
|
self._enable_on_reconnect_data=("toggle-play","stop","clear","update","repeat","random","single","consume","single-oneshot")
|
||||||
self._data=self._disable_on_stop_data+self._enable_on_reconnect_data
|
self._data=self._disable_on_stop_data+self._enable_on_reconnect_data
|
||||||
for name in self._data:
|
for name in self._data:
|
||||||
@ -3063,6 +3063,9 @@ class MPDActionGroup(Gio.SimpleActionGroup):
|
|||||||
def _on_seek_backward(self, action, param):
|
def _on_seek_backward(self, action, param):
|
||||||
self._client.seekcur("-10")
|
self._client.seekcur("-10")
|
||||||
|
|
||||||
|
def _on_clean(self, action, param):
|
||||||
|
self._client.files_to_playlist([self._client.currentsong()["file"]], "enqueue")
|
||||||
|
|
||||||
def _on_clear(self, action, param):
|
def _on_clear(self, action, param):
|
||||||
self._client.clear()
|
self._client.clear()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user