WIP
This commit is contained in:
@@ -18,11 +18,13 @@ try:
|
||||
except Exception:
|
||||
tap_imported = False
|
||||
|
||||
import gi
|
||||
from gi.repository import Gdk, GdkPixbuf, Gio, GLib, Gtk
|
||||
|
||||
try:
|
||||
import gi
|
||||
gi.require_version('Handy', '1')
|
||||
from gi.repository import Handy
|
||||
|
||||
try:
|
||||
gi.require_version("Notify", "0.7")
|
||||
from gi.repository import Notify
|
||||
|
||||
@@ -59,6 +61,8 @@ class SublimeMusicApp(Gtk.Application):
|
||||
if glib_notify_exists:
|
||||
Notify.init("Sublime Music")
|
||||
|
||||
Handy.init()
|
||||
|
||||
self.window: Optional[Gtk.Window] = None
|
||||
self.app_config = AppConfiguration.load_from_file(config_file)
|
||||
self.dbus_manager: Optional[DBusManager] = None
|
||||
|
@@ -4,7 +4,7 @@ import logging
|
||||
import math
|
||||
from typing import Any, Callable, cast, Iterable, List, Optional, Tuple
|
||||
|
||||
from gi.repository import Gdk, Gio, GLib, GObject, Gtk, Pango
|
||||
from gi.repository import Gdk, Gio, GLib, GObject, Gtk, Pango, Handy
|
||||
|
||||
from ..adapters import (
|
||||
AdapterManager,
|
||||
@@ -71,6 +71,8 @@ class AlbumsPanel(Gtk.Box):
|
||||
def __init__(self):
|
||||
super().__init__(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
leaflet = Handy.Leaflet(transition_type=Handy.LeafletTransitionType.SLIDE, can_swipe_forward=False)
|
||||
|
||||
actionbar = Gtk.ActionBar()
|
||||
|
||||
# Sort by
|
||||
@@ -160,6 +162,7 @@ class AlbumsPanel(Gtk.Box):
|
||||
actionbar.pack_end(Gtk.Label(label="Show"))
|
||||
|
||||
# self.add(actionbar)
|
||||
leaflet.add(actionbar)
|
||||
|
||||
scrolled_window = Gtk.ScrolledWindow()
|
||||
self.grid = AlbumsGrid()
|
||||
|
@@ -4,10 +4,6 @@ from typing import Any, Callable, Dict, Optional, Set, Tuple
|
||||
|
||||
import bleach
|
||||
|
||||
import gi
|
||||
from gi.repository import GIRepository
|
||||
GIRepository.Repository.prepend_search_path('/usr/local/lib/x86_64-linux-gnu/girepository-1.0')
|
||||
gi.require_version('Handy', '1')
|
||||
from gi.repository import Gdk, GLib, GObject, Gtk, Pango, Handy
|
||||
|
||||
from ..adapters import (
|
||||
@@ -19,7 +15,7 @@ from ..adapters import (
|
||||
from ..config import AppConfiguration, ProviderConfiguration
|
||||
from ..players import PlayerManager
|
||||
from ..ui import albums, artists, browse, player_controls, playlists, util
|
||||
from ..ui.common import IconButton, IconMenuButton, SpinnerImage
|
||||
from ..ui.common import IconButton, IconToggleButton, IconMenuButton, SpinnerImage
|
||||
|
||||
|
||||
class MainWindow(Gtk.ApplicationWindow):
|
||||
@@ -49,10 +45,27 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
_pending_downloads_label: Optional[Gtk.Label] = None
|
||||
_current_downloads_placeholder: Optional[Gtk.Label] = None
|
||||
|
||||
# Settings
|
||||
setting_song_play_notification = GObject.Property(type=bool, default=False)
|
||||
setting_allow_song_downloads = GObject.Property(type=bool, default=False)
|
||||
setting_download_on_stream = GObject.Property(type=bool, default=False)
|
||||
setting_prefetch_amount = GObject.Property(type=int, default=0)
|
||||
setting_concurrent_download_limit = GObject.Property(type=int, default=0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# self.set_default_size(1342, 756)
|
||||
|
||||
def connect_setting(setting):
|
||||
prop = setting.replace("_", "-")
|
||||
self.connect(f"notify::setting-{prop}",
|
||||
partial(self._on_settings_change, setting))
|
||||
connect_setting("song_play_notification")
|
||||
connect_setting("allow_song_downloads")
|
||||
connect_setting("download_on_stream")
|
||||
connect_setting("prefetch_amount")
|
||||
connect_setting("concurrent_download_limit")
|
||||
|
||||
# Create the stack
|
||||
self.albums_panel = albums.AlbumsPanel()
|
||||
self.artists_panel = artists.ArtistsPanel()
|
||||
@@ -60,16 +73,22 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
self.playlists_panel = playlists.PlaylistsPanel()
|
||||
self.stack = self._create_stack(
|
||||
Albums=self.albums_panel,
|
||||
# Artists=self.artists_panel,
|
||||
# Browse=self.browse_panel,
|
||||
# Playlists=self.playlists_panel,
|
||||
Artists=self.artists_panel,
|
||||
Browse=self.browse_panel,
|
||||
Playlists=self.playlists_panel,
|
||||
)
|
||||
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
|
||||
|
||||
self.titlebar = self._create_headerbar(self.stack)
|
||||
# self.set_titlebar(self.titlebar)
|
||||
self.sidebar_flap = Handy.Flap(
|
||||
orientation=Gtk.Orientation.HORIZONTAL,
|
||||
fold_policy=Handy.FlapFoldPolicy.ALWAYS,
|
||||
transition_type=Handy.FlapTransitionType.OVER,
|
||||
modal=True)
|
||||
|
||||
flap = Handy.Flap(orientation=Gtk.Orientation.VERTICAL, fold_policy=Handy.FlapFoldPolicy.ALWAYS, flap_position=Gtk.PackType.END)
|
||||
self.titlebar = self._create_headerbar(self.stack)
|
||||
self.set_titlebar(self.titlebar)
|
||||
|
||||
drawer = Handy.Flap(orientation=Gtk.Orientation.VERTICAL, fold_policy=Handy.FlapFoldPolicy.ALWAYS, flap_position=Gtk.PackType.END)
|
||||
notification_container = Gtk.Overlay()
|
||||
|
||||
notification_container.add(self.stack)
|
||||
@@ -96,7 +115,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
self.notification_revealer.add(notification_box)
|
||||
|
||||
notification_container.add_overlay(self.notification_revealer)
|
||||
flap.set_content(notification_container)
|
||||
drawer.set_content(notification_container)
|
||||
|
||||
# Player state
|
||||
self.player_manager = player_controls.Manager()
|
||||
@@ -112,7 +131,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
)
|
||||
|
||||
# Player Controls
|
||||
flap.set_separator(Gtk.Separator())
|
||||
drawer.set_separator(Gtk.Separator())
|
||||
|
||||
squeezer = Handy.Squeezer(vexpand=True, homogeneous=False)
|
||||
|
||||
@@ -121,13 +140,13 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
|
||||
mobile_handle = player_controls.MobileHandle(self.player_manager)
|
||||
|
||||
# Toggle flap when handle is pressed
|
||||
def toggle_flap(handle, event):
|
||||
if event.get_click_count() != (True, 1) or not flap.get_swipe_to_open():
|
||||
# Toggle drawer when handle is pressed
|
||||
def toggle_drawer(handle, event):
|
||||
if event.get_click_count() != (True, 1) or not drawer.get_swipe_to_open():
|
||||
return
|
||||
|
||||
flap.set_reveal_flap(not flap.get_reveal_flap())
|
||||
mobile_handle.connect("button-press-event", toggle_flap)
|
||||
drawer.set_reveal_flap(not drawer.get_reveal_flap())
|
||||
mobile_handle.connect("button-press-event", toggle_drawer)
|
||||
|
||||
squeezer.add(mobile_handle)
|
||||
|
||||
@@ -135,27 +154,31 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
def squeezer_changed(squeezer, _):
|
||||
is_desktop = squeezer.get_visible_child() == desktop_controls
|
||||
|
||||
flap.set_swipe_to_open(not is_desktop)
|
||||
drawer.set_swipe_to_open(not is_desktop)
|
||||
|
||||
# When transitioning, don't play the reveal animation
|
||||
dur = flap.get_reveal_duration()
|
||||
flap.set_reveal_duration(0)
|
||||
dur = drawer.get_reveal_duration()
|
||||
drawer.set_reveal_duration(0)
|
||||
|
||||
flap.set_reveal_flap(False)
|
||||
drawer.set_reveal_flap(False)
|
||||
|
||||
flap.set_reveal_duration(dur)
|
||||
drawer.set_reveal_duration(dur)
|
||||
squeezer.connect("notify::visible-child", squeezer_changed)
|
||||
|
||||
flap.set_handle(squeezer)
|
||||
drawer.set_handle(squeezer)
|
||||
|
||||
mobile_flap = player_controls.MobileFlap(self.player_manager)
|
||||
flap.set_flap(mobile_flap)
|
||||
drawer.set_flap(mobile_flap)
|
||||
|
||||
def flap_reveal_progress(flap, _):
|
||||
self.player_manager.flap_open = flap.get_reveal_progress() > 0.5
|
||||
flap.connect("notify::reveal-progress", flap_reveal_progress)
|
||||
def drawer_reveal_progress(drawer, _):
|
||||
self.player_manager.flap_open = drawer.get_reveal_progress() > 0.5
|
||||
drawer.connect("notify::reveal-progress", drawer_reveal_progress)
|
||||
|
||||
self.add(flap)
|
||||
self.sidebar_flap.set_content(drawer)
|
||||
|
||||
self.sidebar_flap.set_flap(self._create_sidebar())
|
||||
|
||||
self.add(self.sidebar_flap)
|
||||
|
||||
self.connect("button-release-event", self._on_button_release)
|
||||
|
||||
@@ -254,156 +277,147 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
|
||||
self.provider_options_box.show_all()
|
||||
|
||||
# Main Settings
|
||||
self.notification_switch.set_active(app_config.song_play_notification)
|
||||
|
||||
# Player settings
|
||||
for c in self.player_settings_box.get_children():
|
||||
self.player_settings_box.remove(c)
|
||||
|
||||
def emit_player_settings_change(
|
||||
player_name: str, option_name: str, value_extraction_fn: Callable, *args
|
||||
):
|
||||
if self._updating_settings:
|
||||
return
|
||||
self.emit(
|
||||
"refresh-window",
|
||||
{
|
||||
"__player_setting__": (
|
||||
player_name,
|
||||
option_name,
|
||||
value_extraction_fn(*args),
|
||||
)
|
||||
},
|
||||
False,
|
||||
)
|
||||
|
||||
for player_name, options in player_manager.get_configuration_options().items():
|
||||
self.player_settings_box.add(Gtk.Separator())
|
||||
self.player_settings_box.add(
|
||||
self._create_label(
|
||||
f"{player_name} Settings", name="menu-settings-separator"
|
||||
)
|
||||
)
|
||||
|
||||
for option_name, descriptor in options.items():
|
||||
setting_box = Gtk.Box()
|
||||
setting_box.add(option_name_label := Gtk.Label(label=option_name))
|
||||
option_name_label.get_style_context().add_class("menu-label")
|
||||
|
||||
option_value = app_config.player_config.get(player_name, {}).get(
|
||||
option_name
|
||||
)
|
||||
|
||||
if type(descriptor) == tuple:
|
||||
option_store = Gtk.ListStore(str)
|
||||
for option in descriptor:
|
||||
option_store.append([option])
|
||||
combo = Gtk.ComboBox.new_with_model(option_store)
|
||||
combo.set_id_column(0)
|
||||
renderer_text = Gtk.CellRendererText()
|
||||
combo.pack_start(renderer_text, True)
|
||||
combo.add_attribute(renderer_text, "text", 0)
|
||||
combo.set_active_id(option_value)
|
||||
combo.connect(
|
||||
"changed",
|
||||
partial(
|
||||
emit_player_settings_change,
|
||||
player_name,
|
||||
option_name,
|
||||
lambda c: c.get_active_id(),
|
||||
),
|
||||
)
|
||||
|
||||
setting_box.pack_end(combo, False, False, 0)
|
||||
|
||||
elif descriptor == bool:
|
||||
switch = Gtk.Switch(active=option_value)
|
||||
switch.connect(
|
||||
"notify::active",
|
||||
partial(
|
||||
emit_player_settings_change,
|
||||
player_name,
|
||||
option_name,
|
||||
lambda s, _: s.get_active(),
|
||||
),
|
||||
)
|
||||
setting_box.pack_end(switch, False, False, 0)
|
||||
|
||||
elif descriptor == int:
|
||||
int_editor_box = Gtk.Box()
|
||||
|
||||
def restrict_to_ints(
|
||||
entry: Gtk.Entry, text: str, length: int, position: int
|
||||
) -> bool:
|
||||
if self._updating_settings:
|
||||
return False
|
||||
if not text.isdigit():
|
||||
entry.emit_stop_by_name("insert-text")
|
||||
return True
|
||||
return False
|
||||
|
||||
entry = Gtk.Entry(width_chars=8, text=option_value, sensitive=False)
|
||||
entry.connect("insert-text", restrict_to_ints)
|
||||
int_editor_box.add(entry)
|
||||
|
||||
buttons_box = Gtk.Box()
|
||||
|
||||
edit_button = IconButton("document-edit-symbolic", relief=True)
|
||||
confirm_button = IconButton("object-select-symbolic", relief=True)
|
||||
cancel_button = IconButton("process-stop-symbolic", relief=True)
|
||||
|
||||
def on_edit_button_click(*a):
|
||||
entry.set_sensitive(True)
|
||||
buttons_box.remove(edit_button)
|
||||
buttons_box.add(cancel_button)
|
||||
buttons_box.add(confirm_button)
|
||||
buttons_box.show_all()
|
||||
|
||||
def on_cancel_button_click(*a):
|
||||
entry.set_text(str(option_value))
|
||||
entry.set_sensitive(False)
|
||||
buttons_box.remove(cancel_button)
|
||||
buttons_box.remove(confirm_button)
|
||||
buttons_box.add(edit_button)
|
||||
buttons_box.show_all()
|
||||
|
||||
edit_button.connect("clicked", on_edit_button_click)
|
||||
confirm_button.connect(
|
||||
"clicked",
|
||||
partial(
|
||||
emit_player_settings_change,
|
||||
player_name,
|
||||
option_name,
|
||||
lambda b: int(entry.get_text()),
|
||||
),
|
||||
)
|
||||
cancel_button.connect("clicked", on_cancel_button_click)
|
||||
buttons_box.add(edit_button)
|
||||
|
||||
int_editor_box.add(buttons_box)
|
||||
|
||||
setting_box.pack_end(int_editor_box, False, False, 0)
|
||||
|
||||
setting_box.get_style_context().add_class("menu-button")
|
||||
self.player_settings_box.add(setting_box)
|
||||
|
||||
self.player_settings_box.show_all()
|
||||
|
||||
# Download Settings
|
||||
allow_song_downloads = app_config.allow_song_downloads
|
||||
self.allow_song_downloads_switch.set_active(allow_song_downloads)
|
||||
self.download_on_stream_switch.set_active(app_config.download_on_stream)
|
||||
self.prefetch_songs_entry.set_value(app_config.prefetch_amount)
|
||||
self.max_concurrent_downloads_entry.set_value(
|
||||
app_config.concurrent_download_limit
|
||||
)
|
||||
self.download_on_stream_switch.set_sensitive(allow_song_downloads)
|
||||
self.prefetch_songs_entry.set_sensitive(allow_song_downloads)
|
||||
self.max_concurrent_downloads_entry.set_sensitive(allow_song_downloads)
|
||||
self.setting_song_play_notification = app_config.song_play_notification
|
||||
self.setting_allow_song_downloads = app_config.allow_song_downloads
|
||||
self.setting_download_on_stream = app_config.download_on_stream
|
||||
self.setting_prefetch_amount = app_config.prefetch_amount
|
||||
self.setting_concurrent_download_limit = app_config.concurrent_download_limit
|
||||
|
||||
self._updating_settings = False
|
||||
|
||||
# Player settings
|
||||
# for c in self.player_settings_box.get_children():
|
||||
# self.player_settings_box.remove(c)
|
||||
|
||||
# def emit_player_settings_change(
|
||||
# player_name: str, option_name: str, value_extraction_fn: Callable, *args
|
||||
# ):
|
||||
# if self._updating_settings:
|
||||
# return
|
||||
# self.emit(
|
||||
# "refresh-window",
|
||||
# {
|
||||
# "__player_setting__": (
|
||||
# player_name,
|
||||
# option_name,
|
||||
# value_extraction_fn(*args),
|
||||
# )
|
||||
# },
|
||||
# False,
|
||||
# )
|
||||
|
||||
# for player_name, options in player_manager.get_configuration_options().items():
|
||||
# self.player_settings_box.add(Gtk.Separator())
|
||||
# self.player_settings_box.add(
|
||||
# self._create_label(
|
||||
# f"{player_name} Settings", name="menu-settings-separator"
|
||||
# )
|
||||
# )
|
||||
|
||||
# for option_name, descriptor in options.items():
|
||||
# setting_box = Gtk.Box()
|
||||
# setting_box.add(option_name_label := Gtk.Label(label=option_name))
|
||||
# option_name_label.get_style_context().add_class("menu-label")
|
||||
|
||||
# option_value = app_config.player_config.get(player_name, {}).get(
|
||||
# option_name
|
||||
# )
|
||||
|
||||
# if type(descriptor) == tuple:
|
||||
# option_store = Gtk.ListStore(str)
|
||||
# for option in descriptor:
|
||||
# option_store.append([option])
|
||||
# combo = Gtk.ComboBox.new_with_model(option_store)
|
||||
# combo.set_id_column(0)
|
||||
# renderer_text = Gtk.CellRendererText()
|
||||
# combo.pack_start(renderer_text, True)
|
||||
# combo.add_attribute(renderer_text, "text", 0)
|
||||
# combo.set_active_id(option_value)
|
||||
# combo.connect(
|
||||
# "changed",
|
||||
# partial(
|
||||
# emit_player_settings_change,
|
||||
# player_name,
|
||||
# option_name,
|
||||
# lambda c: c.get_active_id(),
|
||||
# ),
|
||||
# )
|
||||
|
||||
# setting_box.pack_end(combo, False, False, 0)
|
||||
|
||||
# elif descriptor == bool:
|
||||
# switch = Gtk.Switch(active=option_value)
|
||||
# switch.connect(
|
||||
# "notify::active",
|
||||
# partial(
|
||||
# emit_player_settings_change,
|
||||
# player_name,
|
||||
# option_name,
|
||||
# lambda s, _: s.get_active(),
|
||||
# ),
|
||||
# )
|
||||
# setting_box.pack_end(switch, False, False, 0)
|
||||
|
||||
# elif descriptor == int:
|
||||
# int_editor_box = Gtk.Box()
|
||||
|
||||
# def restrict_to_ints(
|
||||
# entry: Gtk.Entry, text: str, length: int, position: int
|
||||
# ) -> bool:
|
||||
# if self._updating_settings:
|
||||
# return False
|
||||
# if not text.isdigit():
|
||||
# entry.emit_stop_by_name("insert-text")
|
||||
# return True
|
||||
# return False
|
||||
|
||||
# entry = Gtk.Entry(width_chars=8, text=option_value, sensitive=False)
|
||||
# entry.connect("insert-text", restrict_to_ints)
|
||||
# int_editor_box.add(entry)
|
||||
|
||||
# buttons_box = Gtk.Box()
|
||||
|
||||
# edit_button = IconButton("document-edit-symbolic", relief=True)
|
||||
# confirm_button = IconButton("object-select-symbolic", relief=True)
|
||||
# cancel_button = IconButton("process-stop-symbolic", relief=True)
|
||||
|
||||
# def on_edit_button_click(*a):
|
||||
# entry.set_sensitive(True)
|
||||
# buttons_box.remove(edit_button)
|
||||
# buttons_box.add(cancel_button)
|
||||
# buttons_box.add(confirm_button)
|
||||
# buttons_box.show_all()
|
||||
|
||||
# def on_cancel_button_click(*a):
|
||||
# entry.set_text(str(option_value))
|
||||
# entry.set_sensitive(False)
|
||||
# buttons_box.remove(cancel_button)
|
||||
# buttons_box.remove(confirm_button)
|
||||
# buttons_box.add(edit_button)
|
||||
# buttons_box.show_all()
|
||||
|
||||
# edit_button.connect("clicked", on_edit_button_click)
|
||||
# confirm_button.connect(
|
||||
# "clicked",
|
||||
# partial(
|
||||
# emit_player_settings_change,
|
||||
# player_name,
|
||||
# option_name,
|
||||
# lambda b: int(entry.get_text()),
|
||||
# ),
|
||||
# )
|
||||
# cancel_button.connect("clicked", on_cancel_button_click)
|
||||
# buttons_box.add(edit_button)
|
||||
|
||||
# int_editor_box.add(buttons_box)
|
||||
|
||||
# setting_box.pack_end(int_editor_box, False, False, 0)
|
||||
|
||||
# setting_box.get_style_context().add_class("menu-button")
|
||||
# self.player_settings_box.add(setting_box)
|
||||
|
||||
# self.player_settings_box.show_all()
|
||||
|
||||
self.stack.set_visible_child_name(app_config.state.current_tab)
|
||||
|
||||
active_panel = self.stack.get_visible_child()
|
||||
@@ -556,7 +570,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
)
|
||||
|
||||
def _create_stack(self, **kwargs: Gtk.Widget) -> Gtk.Stack:
|
||||
stack = Gtk.Stack()
|
||||
stack = Gtk.Stack(homogeneous=True)
|
||||
for name, child in kwargs.items():
|
||||
child.connect(
|
||||
"song-clicked",
|
||||
@@ -573,9 +587,12 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
"""
|
||||
Configure the header bar for the window.
|
||||
"""
|
||||
header = Handy.HeaderBar()
|
||||
header.set_show_close_button(True)
|
||||
header.props.title = "Sublime Music"
|
||||
squeezer = Handy.Squeezer()
|
||||
|
||||
# Desktop header
|
||||
desktop_header = Handy.HeaderBar()
|
||||
desktop_header.set_show_close_button(True)
|
||||
desktop_header.props.title = "Sublime Music"
|
||||
|
||||
# Search
|
||||
self.search_entry = Gtk.SearchEntry(placeholder_text="Search everything...")
|
||||
@@ -586,14 +603,14 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
self.search_entry.connect("focus-out-event", self._on_search_entry_loose_focus)
|
||||
self.search_entry.connect("changed", self._on_search_entry_changed)
|
||||
self.search_entry.connect("stop-search", self._on_search_entry_stop_search)
|
||||
header.pack_start(self.search_entry)
|
||||
# desktop_header.pack_start(self.search_entry)
|
||||
|
||||
# Search popup
|
||||
self._create_search_popup()
|
||||
|
||||
# Stack switcher
|
||||
switcher = Gtk.StackSwitcher(stack=stack)
|
||||
header.set_custom_title(switcher)
|
||||
desktop_header.set_custom_title(switcher)
|
||||
|
||||
button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||
|
||||
@@ -608,16 +625,14 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
self.downloads_popover.set_relative_to(self.downloads_menu_button)
|
||||
button_box.add(self.downloads_menu_button)
|
||||
|
||||
# Menu button
|
||||
self.main_menu_popover = self._create_main_menu()
|
||||
main_menu_button = IconMenuButton(
|
||||
# Preferences
|
||||
preferences_button = IconButton(
|
||||
"emblem-system-symbolic",
|
||||
tooltip_text="Open Sublime Music settings",
|
||||
popover=self.main_menu_popover,
|
||||
relief=True,
|
||||
)
|
||||
main_menu_button.connect("clicked", self._on_main_menu_clicked)
|
||||
self.main_menu_popover.set_relative_to(main_menu_button)
|
||||
button_box.add(main_menu_button)
|
||||
preferences_button.connect("clicked", self._show_settings)
|
||||
button_box.add(preferences_button)
|
||||
|
||||
# Server icon and change server dropdown
|
||||
self.server_connection_popover = self._create_server_connection_popover()
|
||||
@@ -634,9 +649,56 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
)
|
||||
button_box.add(self.server_connection_menu_button)
|
||||
|
||||
header.pack_end(button_box)
|
||||
desktop_header.pack_end(button_box)
|
||||
|
||||
return header
|
||||
squeezer.add(desktop_header)
|
||||
|
||||
# Mobile header
|
||||
mobile_header = Handy.HeaderBar()
|
||||
mobile_header.set_show_close_button(True)
|
||||
|
||||
button = IconToggleButton("open-menu-symbolic")
|
||||
self.sidebar_flap.bind_property("reveal-flap", button, "active", GObject.BindingFlags.BIDIRECTIONAL)
|
||||
mobile_header.pack_start(button)
|
||||
|
||||
squeezer.add(mobile_header)
|
||||
|
||||
def squeezer_changed(squeezer, _):
|
||||
is_desktop = squeezer.get_visible_child() == desktop_header
|
||||
|
||||
self.sidebar_flap.set_swipe_to_open(not is_desktop)
|
||||
|
||||
# When transitioning, don't play the reveal animation
|
||||
dur = self.sidebar_flap.get_reveal_duration()
|
||||
self.sidebar_flap.set_reveal_duration(0)
|
||||
|
||||
self.sidebar_flap.set_reveal_flap(False)
|
||||
|
||||
self.sidebar_flap.set_reveal_duration(dur)
|
||||
squeezer.connect("notify::visible-child", squeezer_changed)
|
||||
|
||||
return squeezer
|
||||
|
||||
def _create_sidebar(self):
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, width_request=150)
|
||||
box.get_style_context().add_class("background")
|
||||
|
||||
stack = Gtk.StackSidebar(stack=self.stack, vexpand=True)
|
||||
box.pack_start(stack, True, True, 0)
|
||||
|
||||
box.pack_end(Gtk.Separator(), False, False, 0)
|
||||
|
||||
servers = IconButton("list-add-symbolic", label="Servers")
|
||||
box.pack_end(servers, False, False, 0)
|
||||
|
||||
settings = IconButton("emblem-system-symbolic", label="Settings")
|
||||
settings.connect("clicked", self._show_settings)
|
||||
box.pack_end(settings, False, False, 0)
|
||||
|
||||
downloads = IconButton("folder-download-symbolic", label="Downloads")
|
||||
box.pack_end(downloads, False, False, 0)
|
||||
|
||||
return box
|
||||
|
||||
def _create_label(
|
||||
self, text: str, *args, halign: Gtk.Align = Gtk.Align.START, **kwargs
|
||||
@@ -712,23 +774,6 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
|
||||
return box
|
||||
|
||||
def _create_spin_button_menu_item(
|
||||
self, label: str, low: int, high: int, step: int, settings_name: str
|
||||
) -> Tuple[Gtk.Box, Gtk.Entry]:
|
||||
def on_change(entry: Gtk.SpinButton) -> bool:
|
||||
self._emit_settings_change({settings_name: int(entry.get_value())})
|
||||
return False
|
||||
|
||||
box = Gtk.Box()
|
||||
box.add(spin_button_label := Gtk.Label(label=label))
|
||||
spin_button_label.get_style_context().add_class("menu-label")
|
||||
|
||||
entry = Gtk.SpinButton.new_with_range(low, high, step)
|
||||
entry.connect("value-changed", on_change)
|
||||
box.pack_end(entry, False, False, 0)
|
||||
box.get_style_context().add_class("menu-button")
|
||||
return box, entry
|
||||
|
||||
def _create_downloads_popover(self) -> Gtk.PopoverMenu:
|
||||
menu = Gtk.PopoverMenu()
|
||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, name="downloads-menu")
|
||||
@@ -860,66 +905,74 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
menu.child_set_property(switch_provider_options, "submenu", "switch-provider")
|
||||
return menu
|
||||
|
||||
def _create_main_menu(self) -> Gtk.PopoverMenu:
|
||||
main_menu = Gtk.PopoverMenu()
|
||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, name="main-menu-box")
|
||||
def _create_settings_window(self) -> Gtk.PopoverMenu:
|
||||
window = Handy.PreferencesWindow()
|
||||
|
||||
general = Handy.PreferencesPage(icon_name="emblem-system-symbolic", title="General")
|
||||
general_group = Handy.PreferencesGroup(title="General")
|
||||
|
||||
def create_switch(setting, **kwargs):
|
||||
row = Handy.ActionRow(**kwargs)
|
||||
switch = Gtk.Switch(valign=Gtk.Align.CENTER)
|
||||
prop = setting.replace("_", "-")
|
||||
self.bind_property(f"setting-{prop}", switch, "active", GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE)
|
||||
|
||||
row.add(switch)
|
||||
return row
|
||||
|
||||
def create_spin_button(low: int, high: int, step: int, setting: str, **kwargs):
|
||||
row = Handy.ActionRow(**kwargs)
|
||||
button = Gtk.SpinButton.new_with_range(low, high, step)
|
||||
prop = setting.replace("_", "-")
|
||||
self.bind_property(f"setting-{prop}", button, "value", GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE)
|
||||
|
||||
row.add(button)
|
||||
return row
|
||||
|
||||
# Notifications
|
||||
notifications_box, self.notification_switch = self._create_toggle_menu_button(
|
||||
"Enable Song Notifications", "song_play_notification"
|
||||
)
|
||||
vbox.add(notifications_box)
|
||||
row = create_switch("song_play_notification", title="Enable Song Notifications")
|
||||
general_group.add(row)
|
||||
|
||||
# PLAYER SETTINGS
|
||||
# ==============================================================================
|
||||
# Player settings
|
||||
self.player_settings_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
vbox.add(self.player_settings_box)
|
||||
# vbox.add(self.player_settings_box)
|
||||
|
||||
general.add(general_group)
|
||||
window.add(general)
|
||||
|
||||
# DOWNLOAD SETTINGS
|
||||
# ==============================================================================
|
||||
vbox.add(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL))
|
||||
vbox.add(
|
||||
self._create_label("Download Settings", name="menu-settings-separator")
|
||||
)
|
||||
download = Handy.PreferencesPage(icon_name="folder-download-symbolic", title="Downloads")
|
||||
download_group = Handy.PreferencesGroup(title="Downloads", description="Settings for downloads")
|
||||
|
||||
# Allow Song Downloads
|
||||
(
|
||||
allow_song_downloads,
|
||||
self.allow_song_downloads_switch,
|
||||
) = self._create_toggle_menu_button(
|
||||
"Allow Song Downloads", "allow_song_downloads"
|
||||
)
|
||||
vbox.add(allow_song_downloads)
|
||||
download_row = Handy.ExpanderRow(show_enable_switch=True, title="Allow Song Downloads", expanded=True)
|
||||
self.bind_property("setting-allow-song-downloads", download_row, "enable-expansion", GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE)
|
||||
|
||||
# Download on Stream
|
||||
(
|
||||
download_on_stream,
|
||||
self.download_on_stream_switch,
|
||||
) = self._create_toggle_menu_button(
|
||||
"When Streaming, Also Download Song", "download_on_stream"
|
||||
)
|
||||
vbox.add(download_on_stream)
|
||||
row = create_switch("download_on_stream", title="When Streaming, Also Download Song")
|
||||
download_row.add(row)
|
||||
|
||||
# Prefetch Songs
|
||||
(
|
||||
prefetch_songs_box,
|
||||
self.prefetch_songs_entry,
|
||||
) = self._create_spin_button_menu_item(
|
||||
"Number of Songs to Prefetch", 0, 10, 1, "prefetch_amount"
|
||||
)
|
||||
vbox.add(prefetch_songs_box)
|
||||
row = create_spin_button(0, 10, 1, "prefetch_amount", title="Number of Songs to Prefetch")
|
||||
download_row.add(row)
|
||||
|
||||
# Max Concurrent Downloads
|
||||
(
|
||||
max_concurrent_downloads,
|
||||
self.max_concurrent_downloads_entry,
|
||||
) = self._create_spin_button_menu_item(
|
||||
"Maximum Concurrent Downloads", 0, 10, 1, "concurrent_download_limit"
|
||||
)
|
||||
vbox.add(max_concurrent_downloads)
|
||||
row = create_spin_button(0, 10, 1, "concurrent_download_limit", title="Maximum Concurrent Downloads")
|
||||
download_row.add(row)
|
||||
|
||||
main_menu.add(vbox)
|
||||
return main_menu
|
||||
download_group.add(download_row)
|
||||
download.add(download_group)
|
||||
|
||||
window.add(download)
|
||||
|
||||
return window
|
||||
|
||||
def _on_settings_change(self, setting, _, prop):
|
||||
if self._updating_settings:
|
||||
return
|
||||
|
||||
self._emit_settings_change({setting: self.get_property(prop.name)})
|
||||
|
||||
def _create_search_popup(self) -> Gtk.PopoverMenu:
|
||||
self.search_popup = Gtk.PopoverMenu(modal=False)
|
||||
@@ -1032,9 +1085,10 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
self.server_connection_popover.popup()
|
||||
self.server_connection_popover.show_all()
|
||||
|
||||
def _on_main_menu_clicked(self, *args):
|
||||
self.main_menu_popover.popup()
|
||||
self.main_menu_popover.show_all()
|
||||
def _show_settings(self, *args):
|
||||
window = self._create_settings_window()
|
||||
window.set_transient_for(self)
|
||||
window.show_all()
|
||||
|
||||
def _on_search_entry_focus(self, *args):
|
||||
self._show_search()
|
||||
|
Reference in New Issue
Block a user