Compare commits

...

9 commits

38 changed files with 1812 additions and 2883 deletions

View file

@ -12,6 +12,7 @@ License: [GPL](http://www.gnu.org/licenses/gpl.html) v3
Dependencies: Dependencies:
* [Python](http://www.python.org) 3 * [Python](http://www.python.org) 3
* [python-dateutil](https://pypi.org/project/python-dateutil/)
* [GTK](http://www.gtk.org) 3 (>= 3.22) ([python-gobject](https://live.gnome.org/PyGObject)) * [GTK](http://www.gtk.org) 3 (>= 3.22) ([python-gobject](https://live.gnome.org/PyGObject))
* [Avahi](http://www.avahi.org) (optional) * [Avahi](http://www.avahi.org) (optional)
* [python-keyring](http://pypi.python.org/pypi/keyring) (optional) * [python-keyring](http://pypi.python.org/pypi/keyring) (optional)

View file

@ -1,8 +1,20 @@
.bg-texture { #content_stack {
box-shadow:inset 4px 4px 10px rgba(0,0,0,0.3); box-shadow:inset 4px 4px 10px rgba(0,0,0,0.3);
background-image:url('noise-texture.png'); background-image:url('noise-texture.png');
} }
#port_spinner {
background:none;
margin-top:-13px;
}
#port_spinner text {
padding-left: 0;
}
#server_stack_sidebar {
background-color:alpha(@theme_bg_color, 1);
}
.no-bg { .no-bg {
background:none; background:none;
} }
@ -18,13 +30,17 @@
font-weight:bold; font-weight:bold;
} }
revealer.sidebar > * { window.fullscreen #cover_box {
background-color:alpha(@theme_bg_color, 0.8); background: black;
box-shadow:0 0 10px @theme_bg_color;
margin-left:20px
} }
revealer.sidebar scale mark indicator { #cover_info_revealer {
background-color:alpha(@theme_bg_color, 0.8);
box-shadow:0 0 10px @theme_bg_color;
margin-left:20px;
}
#cover_info_revealer scale mark indicator {
margin-right:5px; margin-right:5px;
} }
@ -32,24 +48,13 @@ actionbar {
background-color:@theme_unfocused_bg_color; background-color:@theme_unfocused_bg_color;
} }
/* Icon View in regular mode */ gridview child {
iconview.view:selected, padding: 1px;
iconview.view:selected:focus {
background-color:@theme_selected_bg_color;
} }
iconview.view:hover { gridview.selection child {
-gtk-icon-effect:highlight; opacity: 0.5;
} }
gridview.selection child:hover,
/* Icon View in selection mode */ gridview.selection child:selected {
iconview.view.selection { opacity: 1;
-gtk-icon-effect:dim;
}
iconview.view.selection:selected,
iconview.view.selection:selected:focus {
background-color:@theme_selected_bg_color;
-gtk-icon-effect:highlight;
}
iconview.view.selection:hover {
-gtk-icon-effect:none;
} }

View file

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.10"/> <requires lib="gtk+" version="4.8"/>
<template class="McgAlbumHeaderbar" parent="GtkHeaderBar"> <requires lib="adw" version="1.2" />
<template class="McgAlbumHeaderbar" parent="AdwBin">
<child>
<object class="GtkHeaderBar">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<child type="title"> <child type="title">
@ -21,11 +23,13 @@
<attribute name="scale" value="1"/> <attribute name="scale" value="1"/>
</attributes> </attributes>
</object> </object>
<!--
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
-->
</child> </child>
<child> <child>
<object class="GtkLabel" id="standalone_artist"> <object class="GtkLabel" id="standalone_artist">
@ -34,11 +38,13 @@
<property name="label" translatable="yes">Artist</property> <property name="label" translatable="yes">Artist</property>
<property name="selectable">True</property> <property name="selectable">True</property>
</object> </object>
<!--
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
-->
</child> </child>
</object> </object>
</child> </child>
@ -57,5 +63,7 @@
</child> </child>
</object> </object>
</child> </child>
</template> </object>
</child>
</template>
</interface> </interface>

View file

@ -1,182 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.12"/> <requires lib="gtk+" version="4.8"/>
<object class="GtkAdjustment" id="server-port-adjustment"> <requires lib="adw" version="1.2" />
<property name="lower">1024</property> <object class="GtkAdjustment" id="server-port-adjustment">
<property name="upper">9999</property> <property name="lower">1024</property>
<property name="value">6600</property> <property name="upper">9999</property>
<property name="step-increment">1</property> <property name="value">6600</property>
<property name="page-increment">100</property> <property name="step-increment">1</property>
</object> <property name="page-increment">100</property>
<template class="McgConnectionPanel" parent="GtkBox"> </object>
<property name="visible">True</property> <object class="GtkBox" id="toolbar">
<property name="can-focus">False</property> <property name="orientation">horizontal</property>
<property name="orientation">vertical</property> <property name="spacing">6</property>
<child> </object>
<object class="GtkBox"> <template class="McgConnectionPanel" parent="AdwBin">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child> <child>
<!-- n-columns=3 n-rows=6 --> <object class="AdwStatusPage">
<object class="GtkGrid"> <property name="title">Connect to MPD</property>
<property name="width-request">500</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="column-spacing">5</property>
<property name="row-homogeneous">True</property>
<property name="column-homogeneous">True</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child> <child>
<object class="GtkTreeView" id="zeroconf_list"> <object class="GtkBox">
<property name="visible">True</property> <property name="hexpand">false</property>
<property name="can-focus">True</property> <property name="halign">center</property>
<signal name="focus-out-event" handler="on_zeroconf_list_outfocused" swapped="no"/> <child>
<child internal-child="selection"> <object class="GtkListBox" id="zeroconf_list">
<object class="GtkTreeSelection"> <property name="hexpand">true</property>
<signal name="changed" handler="on_service_selected" swapped="no"/> <child type="placeholder">
</object> <object class="GtkLabel">
</child> <property name="label" translatable="true">No service found</property>
</object> </object>
<packing> </child>
<property name="expand">True</property> </object>
<property name="fill">True</property> </child>
<property name="position">0</property> <child>
</packing> <object class="GtkListBox">
<property name="hexpand">true</property>
<property name="selection-mode">none</property>
<style>
<class name="boxed-list"/>
</style>
<child>
<object class="AdwEntryRow" id="host_row">
<property name="title" translatable="true">Host</property>
<property name="show-apply-button">true</property>
<signal name="apply" handler="on_host_entry_apply"/>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<style>
<class name="header"/>
</style>
<child>
<object class="GtkLabel">
<property name="label" translatable="true">Port</property>
<property name="halign">start</property>
<property name="hexpand">false</property>
<style>
<class name="subtitle"/>
</style>
</object>
</child>
<child>
<object class="GtkSpinButton" id="port_spinner">
<property name="name">port_spinner</property>
<property name="value">6600</property>
<property name="adjustment">server-port-adjustment</property>
<signal name="value-changed" handler="on_port_spinner_value_changed"/>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPasswordEntryRow" id="password_row">
<property name="title" translatable="true">Password</property>
<property name="show-apply-button">true</property>
</object>
</child>
</object>
</child>
</object>
</child> </child>
<child> </object>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">5</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
<property name="height">6</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="host_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="text">localhost</property>
<property name="placeholder-text" translatable="yes">Enter hostname or IP address</property>
<signal name="focus-out-event" handler="on_host_entry_outfocused" swapped="no"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="password_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="visibility">False</property>
<property name="placeholder-text" translatable="yes">Enter password or leave blank</property>
<property name="input-purpose">password</property>
<signal name="focus-out-event" handler="on_password_entry_outfocused" swapped="no"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="port_spinner">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="text">6600</property>
<property name="input-purpose">number</property>
<property name="adjustment">server-port-adjustment</property>
<property name="value">6600</property>
<signal name="value-changed" handler="on_port_spinner_value_changed" swapped="no"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Host:</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Port:</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Password:</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child> </child>
</object> </template>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</template>
</interface> </interface>

View file

@ -1,199 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.22"/> <requires lib="gtk+" version="4.8"/>
<template class="McgCoverPanel" parent="GtkOverlay"> <requires lib="adw" version="1.2" />
<property name="visible">True</property> <object class="GtkBox" id="toolbar">
<property name="can-focus">False</property> <property name="orientation">horizontal</property>
<child>
<object class="GtkStack" id="cover_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkSpinner" id="cover_spinner">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="name">cover-spinner</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="cover_scroll">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="kinetic-scrolling">False</property>
<property name="overlay-scrolling">False</property>
<signal name="size-allocate" handler="on_cover_size_allocate" swapped="no"/>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkEventBox" id="cover_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<signal name="button-press-event" handler="on_cover_box_pressed" swapped="no"/>
<child>
<object class="GtkImage" id="cover_image">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon_size">0</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="name">cover-scroll</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
<child type="overlay">
<object class="GtkRevealer" id="info_revealer">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">end</property> <property name="halign">end</property>
<property name="transition-type">slide-right</property> <property name="spacing">6</property>
<child> <child>
<object class="GtkScrolledWindow" id="cover_info_scroll"> <object class="GtkButton" id="fullscreen_button">
<property name="visible">True</property> <property name="receives-default">True</property>
<property name="can-focus">True</property> <property name="tooltip-text" translatable="yes">Show the cover in fullscreen mode</property>
<property name="halign">start</property> <property name="action-name">win.toggle-fullscreen</property>
<property name="vscrollbar-policy">never</property>
<property name="max-content-width">200</property>
<property name="propagate-natural-width">True</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="shadow-type">none</property>
<child> <child>
<object class="GtkBox"> <object class="GtkImage">
<property name="visible">True</property> <property name="icon-name">view-fullscreen-symbolic</property>
<property name="can-focus">False</property> </object>
<property name="halign">start</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="orientation">vertical</property>
<child>
<!-- n-columns=3 n-rows=3 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">5</property>
<property name="margin-bottom">5</property>
<property name="row-spacing">5</property>
<property name="column-homogeneous">True</property>
<child>
<object class="GtkLabel" id="album_title_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label">Album</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="album_date_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label">Date</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="album_artist_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label">Artist</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<style>
<class name="cover-labels"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">10</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScale" id="songs_scale">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="orientation">vertical</property>
<property name="restrict-to-fill-level">False</property>
<property name="digits">0</property>
<property name="draw-value">False</property>
<signal name="button-press-event" handler="on_songs_start_change" swapped="no"/>
<signal name="button-release-event" handler="on_songs_change" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child> </child>
</object> </object>
</child>
</object>
</child> </child>
<style> </object>
<class name="sidebar"/> <template class="McgCoverPanel" parent="GtkOverlay">
<class name="background"/> <child>
</style> <object class="GtkStack" id="cover_stack">
</object> <child>
</child> <object class="GtkSpinner" id="cover_spinner">
</template> </object>
</child>
<child>
<object class="GtkImage" id="cover_default">
<property name="icon-name">image-x-generic-symbolic</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="cover_scroll">
<property name="kinetic-scrolling">False</property>
<property name="overlay-scrolling">False</property>
<property name="hexpand">true</property>
<property name="halign">fill</property>
<property name="vexpand">true</property>
<property name="valign">fill</property>
<child>
<object class="GtkViewport" id="cover_box">
<property name="name">cover_box</property>
<property name="hexpand">true</property>
<property name="halign">fill</property>
<property name="vexpand">true</property>
<property name="valign">fill</property>
<child>
<object class="GtkImage" id="cover_image">
<property name="hexpand">true</property>
<property name="halign">fill</property>
<property name="vexpand">true</property>
<property name="valign">fill</property>
<property name="icon-name">image-x-generic-symbolic</property>
<property name="icon-size">large</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child type="overlay">
<object class="GtkRevealer" id="info_revealer">
<property name="halign">end</property>
<property name="transition-type">slide-right</property>
<property name="name">cover_info_revealer</property>
<style>
<class name="background"/>
</style>
<child>
<object class="GtkScrolledWindow" id="cover_info_scroll">
<property name="halign">start</property>
<property name="vscrollbar-policy">never</property>
<property name="max-content-width">200</property>
<property name="propagate-natural-width">True</property>
<property name="name">cover_info_scroll</property>
<child>
<object class="GtkViewport">
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="halign">start</property>
<property name="valign">fill</property>
<property name="vexpand">true</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<child>
<object class="GtkGrid">
<property name="margin-start">5</property>
<property name="margin-bottom">5</property>
<property name="row-spacing">5</property>
<property name="column-homogeneous">True</property>
<style>
<class name="cover-labels"/>
</style>
<child>
<object class="GtkLabel" id="album_title_label">
<property name="halign">start</property>
<property name="label">Album</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="album_date_label">
<property name="halign">start</property>
<property name="label">Date</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="album_artist_label">
<property name="halign">start</property>
<property name="label">Artist</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
<layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScale" id="songs_scale">
<property name="orientation">vertical</property>
<property name="valign">fill</property>
<property name="vexpand">true</property>
<property name="restrict-to-fill-level">False</property>
<property name="digits">0</property>
<property name="draw-value">False</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface> </interface>

View file

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<template class="McgCoverToolbar" parent="GtkButtonBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">6</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="fullscreen_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Show the cover in fullscreen mode</property>
<property name="action-name">win.toggle-fullscreen</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">view-fullscreen-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
<property name="non-homogeneous">True</property>
</packing>
</child>
</template>
</interface>

View file

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<menu id="app-menu">
<section>
<item>
<attribute name="action">win.connect</attribute>
<attribute name="label" translatable="yes">Connect</attribute>
<attribute name="accel">&lt;Primary&gt;c</attribute>
</item>
<item>
<attribute name="action">win.play</attribute>
<attribute name="label" translatable="yes">Play</attribute>
<attribute name="accel">&lt;Primary&gt;p</attribute>
</item>
<item>
<attribute name="action">win.clear-playlist</attribute>
<attribute name="label" translatable="yes">Clear Playlist</attribute>
<attribute name="accel">&lt;Primary&gt;r</attribute>
</item>
<item>
<attribute name="action">win.toggle-fullscreen</attribute>
<attribute name="label" translatable="yes">Toggle Fullscreen</attribute>
<attribute name="accel">F11</attribute>
</item>
<item>
<attribute name="action">win.search-library</attribute>
<attribute name="label" translatable="yes">Search Library</attribute>
<attribute name="accel">&lt;Primary&gt;f</attribute>
</item>
</section>
<section>
<item>
<attribute name="action">win.panel</attribute>
<attribute name="label" translatable="yes">Connection</attribute>
<attribute name="target">0</attribute>
<attribute name="accel">&lt;Primary&gt;KP_1</attribute>
</item>
<item>
<attribute name="action">win.panel</attribute>
<attribute name="label" translatable="yes">Cover</attribute>
<attribute name="target">1</attribute>
<attribute name="accel">&lt;Primary&gt;KP_2</attribute>
</item>
<item>
<attribute name="action">win.panel</attribute>
<attribute name="label" translatable="yes">Playlist</attribute>
<attribute name="target">2</attribute>
<attribute name="accel">&lt;Primary&gt;KP_3</attribute>
</item>
<item>
<attribute name="action">win.panel</attribute>
<attribute name="label" translatable="yes">Library</attribute>
<attribute name="target">3</attribute>
<attribute name="accel">&lt;Primary&gt;KP_4</attribute>
</item>
</section>
<section>
<item>
<attribute name="action">win.show-help-overlay</attribute>
<attribute name="label" translatable="yes">Keyboard Shortcuts</attribute>
<attribute name="accel">&lt;Primary&gt;k</attribute>
</item>
<item>
<attribute name="action">app.info</attribute>
<attribute name="label" translatable="yes">Info</attribute>
<attribute name="accel">&lt;Primary&gt;i</attribute>
</item>
<item>
<attribute name="action">app.quit</attribute>
<attribute name="label" translatable="yes">Quit</attribute>
<attribute name="accel">&lt;Primary&gt;q</attribute>
</item>
</section>
</menu>
</interface>

View file

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<template class="McgInfoDialog" parent="GtkAboutDialog">
<property name="can-focus">False</property>
<property name="resizable">False</property>
<property name="modal">True</property>
<property name="window-position">center</property>
<property name="type-hint">dialog</property>
<property name="gravity">center</property>
<property name="program-name">CoverGrid</property>
<property name="version">3.1</property>
<property name="comments" translatable="yes">CoverGrid is a client for the Music Player Daemon, focusing on albums instead of single tracks.</property>
<property name="website">http://www.suruatoel.xyz/codes/mcg</property>
<property name="logo-icon-name">xyz.suruatoel.mcg</property>
<property name="license-type">gpl-3-0</property>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</template>
</interface>

View file

@ -1,303 +1,339 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.16"/> <requires lib="gtk+" version="4.8"/>
<template class="McgLibraryPanel" parent="GtkStack"> <requires lib="adw" version="1.2" />
<property name="visible">True</property> <object class="GtkAdjustment" id="grid_adjustment">
<property name="can-focus">False</property> <property name="lower">100</property>
<property name="transition-type">slide-left-right</property> <property name="upper">1000</property>
<child> <property name="value">150</property>
<object class="GtkBox" id="panel_normal"> <property name="step-increment">1</property>
<property name="visible">True</property> <property name="page-increment">10</property>
</object>
<object class="GtkPopover" id="toolbar_popover">
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkSearchBar" id="filter_bar"> <object class="GtkBox">
<property name="visible">True</property>
<property name="app-paintable">True</property>
<property name="can-focus">False</property>
<signal name="notify" handler="on_filter_bar_notify" swapped="no"/>
<child>
<object class="GtkSearchEntry" id="filter_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="primary-icon-name">edit-find-symbolic</property>
<property name="primary-icon-activatable">False</property>
<property name="primary-icon-sensitive">False</property>
<property name="placeholder-text" translatable="yes">search library</property>
<signal name="search-changed" handler="on_filter_entry_changed" swapped="no"/>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<object class="GtkStack" id="stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="transition-type">crossfade</property>
<child>
<object class="GtkBox" id="progress_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child type="center"> <property name="halign">end</property>
<object class="GtkBox"> <child>
<property name="visible">True</property> <object class="GtkScale" id="grid_scale">
<property name="can-focus">False</property> <property name="width-request">350</property>
<property name="orientation">vertical</property>
<child type="center">
<object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">True</property>
<property name="halign">center</property> <property name="adjustment">grid_adjustment</property>
<property name="restrict-to-fill-level">False</property>
<property name="fill-level">-1</property>
<property name="round-digits">0</property>
<property name="digits">0</property>
<property name="has-origin">False</property>
<signal name="value-changed" handler="on_grid_scale_changed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkButton" id="library-toolbar-update">
<property name="label" translatable="yes">update library</property>
<signal name="clicked" handler="on_update_clicked" swapped="no"/>
</object>
</child>
<child>
<object class="GtkBox" id="library-toolbar-sort">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">10</property>
<child> <child>
<object class="GtkImage" id="progress_image"> <object class="GtkSeparator">
<property name="visible">True</property> <property name="orientation">vertical</property>
<property name="can-focus">False</property> </object>
<property name="icon_size">6</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkProgressBar" id="progress_bar"> <object class="GtkLabel">
<property name="width-request">200</property> <property name="label" translatable="yes">Sort</property>
<property name="visible">True</property> </object>
<property name="can-focus">False</property>
<property name="pulse-step">0</property>
<property name="show-text">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child> </child>
</object> <child>
<packing> <object class="GtkCheckButton" id="sort_artist">
<property name="expand">False</property> <property name="label" translatable="yes">sort by artist</property>
<property name="fill">True</property> <property name="receives-default">False</property>
<property name="position">1</property> <property name="group">sort_year</property>
</packing> <signal name="toggled" handler="on_sort_toggled" swapped="no"/>
</child> </object>
<child> </child>
<placeholder/> <child>
</child> <object class="GtkCheckButton" id="sort_title">
</object> <property name="label" translatable="yes">sort by title</property>
<packing> <property name="receives-default">False</property>
<property name="expand">False</property> <property name="group">sort_year</property>
<property name="fill">True</property> <signal name="toggled" handler="on_sort_toggled" swapped="no"/>
<property name="position">1</property> </object>
</packing> </child>
<child>
<object class="GtkCheckButton" id="sort_year">
<property name="label" translatable="yes">sort by year</property>
<property name="receives-default">False</property>
<signal name="toggled" handler="on_sort_toggled" swapped="no"/>
</object>
</child>
<child>
<object class="GtkCheckButton" id="sort_modified">
<property name="label" translatable="yes">sort by modification</property>
<property name="receives-default">False</property>
<property name="group">sort_year</property>
<signal name="toggled" handler="on_sort_toggled" swapped="no"/>
</object>
</child>
<child>
<object class="GtkCheckButton" id="toolbar_sort_order_button">
<property name="label">sort library descending</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="active">True</property>
<signal name="toggled" handler="on_sort_order_toggled" swapped="no"/>
</object>
</child>
</object>
</child> </child>
</object>
</child>
</object>
<object class="GtkBox" id="toolbar">
<property name="orientation">horizontal</property>
<property name="halign">end</property>
<property name="spacing">6</property>
<child>
<object class="GtkToggleButton" id="toolbar_search_bar">
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Search the library</property>
<child> <child>
<placeholder/> <object class="GtkImage">
<property name="icon-name">system-search-symbolic</property>
</object>
</child> </child>
</object> <!--
<packing> <accelerator key="f" signal="activate" modifiers="GDK_CONTROL_MASK"/>
<property name="name">page1</property> -->
<property name="title">page1</property> </object>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scroll">
<property name="visible">True</property>
<property name="can-focus">True</property>
<child>
<object class="GtkIconView" id="library_grid">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="margin">0</property>
<property name="item-orientation">horizontal</property>
<property name="row-spacing">0</property>
<property name="column-spacing">0</property>
<property name="tooltip-column">1</property>
<property name="item-padding">0</property>
<property name="activate-on-single-click">True</property>
<signal name="item-activated" handler="on_library_grid_clicked" swapped="no"/>
<signal name="selection-changed" handler="on_library_grid_selection_changed" swapped="no"/>
<signal name="size-allocate" handler="on_resize" swapped="no"/>
<style>
<class name="no-bg"/>
</style>
</object>
</child>
<style>
<class name="no-bg"/>
</style>
</object>
<packing>
<property name="name">page0</property>
<property name="title">page0</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkRevealer" id="actionbar_revealer"> <object class="GtkToggleButton" id="select_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="transition-type">none</property>
<child>
<object class="GtkActionBar" id="library-actionbar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_selection_cancel_clicked" swapped="no"/>
</object>
<packing>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">queue</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_selection_add_clicked" swapped="no"/>
</object>
<packing>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="name">page0</property>
<property name="title">page0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="panel_standalone">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkStack" id="standalone_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="transition-type">crossfade</property>
<child>
<object class="GtkSpinner" id="standalone_spinner">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="name">standalone-spinne</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="standalone_scroll">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="kinetic-scrolling">False</property> <property name="receives-default">True</property>
<property name="overlay-scrolling">False</property> <property name="tooltip-text" translatable="yes">Select multiple albums</property>
<signal name="size-allocate" handler="on_standalone_scroll_size_allocate" swapped="no"/> <signal name="toggled" handler="on_select_toggled" swapped="no"/>
<child> <child>
<object class="GtkViewport"> <object class="GtkImage">
<property name="visible">True</property> <property name="icon-name">object-select-symbolic</property>
<property name="can-focus">False</property> </object>
<child> </child>
<object class="GtkImage" id="standalone_image"> </object>
</child>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Settings and actions</property>
<property name="popover">toolbar_popover</property>
<child>
<object class="GtkImage">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="icon-name">gtk-missing-image</property> <property name="icon-name">open-menu-symbolic</property>
<property name="icon_size">6</property> </object>
</object>
</child>
</object>
</child> </child>
</object> </object>
<packing>
<property name="name">standalone-scroll</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child> </child>
</object>
<template class="McgLibraryPanel" parent="AdwBin">
<child> <child>
<object class="GtkActionBar" id="library-standalone-actionbar"> <object class="GtkStack" id="library_stack">
<property name="visible">True</property> <property name="transition-type">slide-left-right</property>
<property name="can-focus">False</property> <child>
<child> <object class="GtkBox" id="panel_normal">
<object class="GtkButton"> <property name="orientation">vertical</property>
<property name="label" translatable="yes">queue</property> <child>
<property name="visible">True</property> <object class="GtkSearchBar" id="filter_bar">
<property name="can-focus">True</property> <property name="search-mode-enabled" bind-source="toolbar_search_bar" bind-property="active" bind-flags="sync-create"/>
<property name="receives-default">True</property> <child>
<signal name="clicked" handler="on_standalone_queue_clicked" swapped="no"/> <object class="GtkSearchEntry" id="filter_entry">
</object> <property name="placeholder-text" translatable="yes">search library</property>
<packing> <signal name="search-changed" handler="on_filter_entry_changed" swapped="no"/>
<property name="pack-type">end</property> </object>
<property name="position">1</property> </child>
</packing> </object>
</child>
<child>
<object class="GtkStack" id="stack">
<property name="transition-type">crossfade</property>
<child>
<object class="GtkBox" id="progress_box">
<property name="orientation">vertical</property>
<child type="center">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child type="center">
<object class="GtkBox">
<property name="halign">center</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child>
<object class="GtkImage" id="progress_image">
<property name="icon-size">large</property>
<property name="icon-name">image-x-generic-symbolic</property>
</object>
</child>
<child>
<object class="GtkProgressBar" id="progress_bar">
<property name="width-request">200</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="pulse-step">0</property>
<property name="show-text">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="scroll">
<property name="vexpand">true</property>
<child>
<object class="GtkGridView" id="library_grid">
<property name="orientation">vertical</property>
<property name="single_click_activate">true</property>
<signal name="activate" handler="on_library_grid_clicked"/>
<style>
<class name="no-bg"/>
</style>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="bytes">
<![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="activatable">true</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkPicture">
<property name="content-fit">contain</property>
<property name="can-shrink">false</property>
<binding name="tooltip-markup">
<lookup name="tooltip" type="GridItem">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
<binding name="paintable">
<lookup name="cover" type="GridItem">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</child>
</object>
</property>
</template>
</interface>
]]>
</property>
</object>
</property>
</object>
</child>
<style>
<class name="no-bg"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkRevealer" id="actionbar_revealer">
<property name="transition-type">slide-up</property>
<child>
<object class="GtkActionBar" id="actionbar">
<child type="end">
<object class="GtkButton">
<property name="label" translatable="yes">cancel</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_selection_cancel_clicked" swapped="no"/>
</object>
</child>
<child type="end">
<object class="GtkButton">
<property name="label" translatable="yes">queue</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_selection_add_clicked" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child> </child>
<child> <child>
<object class="GtkButton"> <object class="GtkBox" id="panel_standalone">
<property name="label" translatable="yes">play</property> <property name="orientation">vertical</property>
<property name="visible">True</property> <child>
<property name="can-focus">True</property> <object class="GtkStack" id="standalone_stack">
<property name="receives-default">True</property> <property name="vexpand">true</property>
<signal name="clicked" handler="on_standalone_play_clicked" swapped="no"/> <property name="transition-type">crossfade</property>
</object> <child>
<packing> <object class="GtkSpinner" id="standalone_spinner">
<property name="pack-type">end</property> <property name="visible">True</property>
<property name="position">0</property> <property name="can-focus">False</property>
</packing> </object>
</child> </child>
</object> <child>
<packing> <object class="GtkScrolledWindow" id="standalone_scroll">
<property name="expand">False</property> <property name="visible">True</property>
<property name="fill">True</property> <property name="can-focus">True</property>
<property name="position">1</property> <property name="kinetic-scrolling">False</property>
</packing> <property name="overlay-scrolling">False</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkImage" id="standalone_image">
<property name="icon-name">gtk-missing-image</property>
<property name="icon-size">large</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkActionBar" id="actionbar_standalone">
<child type="end">
<object class="GtkButton">
<property name="label" translatable="yes">play</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_standalone_play_clicked" swapped="no"/>
</object>
</child>
<child type="end">
<object class="GtkButton">
<property name="label" translatable="yes">queue</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_standalone_queue_clicked" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child> </child>
</object> </template>
<packing>
<property name="name">page1</property>
<property name="title">page1</property>
<property name="position">1</property>
</packing>
</child>
</template>
</interface> </interface>

View file

@ -1,232 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkAdjustment" id="grid_adjustment">
<property name="lower">100</property>
<property name="upper">1000</property>
<property name="value">150</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
<object class="GtkPopover" id="toolbar_popover">
<property name="can-focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScale" id="grid_scale">
<property name="width-request">350</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="adjustment">grid_adjustment</property>
<property name="restrict-to-fill-level">False</property>
<property name="fill-level">-1</property>
<property name="round-digits">0</property>
<property name="digits">0</property>
<property name="has-origin">False</property>
<signal name="button-release-event" handler="on_grid_scale_changed" swapped="no"/>
<signal name="change-value" handler="on_grid_scale_change" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="library-toolbar-update">
<property name="label">gtk-refresh</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="relief">none</property>
<property name="use-stock">True</property>
<signal name="clicked" handler="on_update_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="library-toolbar-sort">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Sort</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="sort_artist">
<property name="label" translatable="yes">sort by artist</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="draw-indicator">True</property>
<property name="group">sort_year</property>
<signal name="toggled" handler="on_sort_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="sort_title">
<property name="label" translatable="yes">sort by title</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="draw-indicator">True</property>
<property name="group">sort_year</property>
<signal name="toggled" handler="on_sort_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="sort_year">
<property name="label" translatable="yes">sort by year</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
<signal name="toggled" handler="on_sort_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="toolbar_sort_order_button">
<property name="label">gtk-sort-descending</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="use-stock">True</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
<signal name="toggled" handler="on_sort_order_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
<template class="McgLibraryToolbar" parent="GtkButtonBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">6</property>
<property name="layout-style">end</property>
<child>
<object class="GtkToggleButton" id="toolbar_search_bar">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Search the library</property>
<signal name="toggled" handler="on_search_toggled" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">system-search-symbolic</property>
</object>
</child>
<accelerator key="f" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
<property name="non-homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="select_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Select multiple albums</property>
<signal name="toggled" handler="on_select_toggled" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">object-select-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
<property name="non-homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Settings and actions</property>
<property name="popover">toolbar_popover</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">open-menu-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
<property name="non-homogeneous">True</property>
</packing>
</child>
</template>
</interface>

View file

@ -1,194 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.16"/> <requires lib="gtk+" version="4.8"/>
<template class="McgPlaylistPanel" parent="GtkStack"> <requires lib="adw" version="1.2" />
<property name="visible">True</property> <object class="GtkBox" id="toolbar">
<property name="can-focus">False</property> <property name="orientation">horizontal</property>
<property name="transition-type">slide-left-right</property> <property name="halign">end</property>
<child> <property name="spacing">6</property>
<object class="GtkBox" id="panel_normal">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkScrolledWindow"> <object class="GtkToggleButton" id="select_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<child>
<object class="GtkIconView" id="playlist_grid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="margin">0</property> <property name="receives-default">True</property>
<property name="item-orientation">horizontal</property> <property name="tooltip-text" translatable="yes">Select multiple albums</property>
<property name="row-spacing">0</property> <signal name="toggled" handler="on_select_toggled" swapped="no"/>
<property name="column-spacing">0</property> <child>
<property name="tooltip-column">1</property> <object class="GtkImage">
<property name="item-padding">0</property> <property name="icon-name">object-select-symbolic</property>
<property name="activate-on-single-click">True</property> </object>
<signal name="item-activated" handler="on_playlist_grid_clicked" swapped="no"/> </child>
<signal name="selection-changed" handler="on_playlist_grid_selection_changed" swapped="no"/> </object>
<style>
<class name="no-bg"/>
</style>
</object>
</child>
<style>
<class name="no-bg"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkRevealer" id="actionbar_revealer"> <object class="GtkButton" id="playlist_clear_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="transition-type">slide-up</property>
<child>
<object class="GtkActionBar" id="actionbar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_selection_cancel_clicked" swapped="no"/>
</object>
<packing>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">remove</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_selection_remove_clicked" swapped="no"/>
</object>
<packing>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="name">page2</property>
<property name="title">page2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="panel_standalone">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkStack" id="standalone_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkSpinner" id="standalone_spinner">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="name">standalone-spinne</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="standalone_scroll">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="kinetic-scrolling">False</property> <property name="receives-default">True</property>
<property name="overlay-scrolling">False</property> <property name="tooltip-text" translatable="yes">Clear the playlist</property>
<signal name="size-allocate" handler="on_standalone_scroll_size_allocate" swapped="no"/> <signal name="clicked" handler="on_clear_clicked" swapped="no"/>
<child> <child>
<object class="GtkViewport"> <object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkImage" id="standalone_image">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="icon-name">gtk-missing-image</property> <property name="icon-name">edit-clear</property>
<property name="icon_size">6</property> </object>
</object>
</child>
</object>
</child> </child>
</object> </object>
<packing>
<property name="name">standalone-scroll</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child> </child>
</object>
<template class="McgPlaylistPanel" parent="AdwBin">
<child> <child>
<object class="GtkActionBar" id="actionbar_standalone"> <object class="GtkStack" id="playlist_stack">
<property name="visible">True</property> <property name="transition-type">slide-left-right</property>
<property name="can-focus">False</property> <child>
<child> <object class="GtkBox" id="panel_normal">
<object class="GtkButton"> <property name="orientation">vertical</property>
<property name="label" translatable="yes">remove</property> <child>
<property name="visible">True</property> <object class="GtkScrolledWindow">
<property name="can-focus">True</property> <property name="vexpand">true</property>
<property name="receives-default">True</property> <child>
<signal name="clicked" handler="on_standalone_remove_clicked" swapped="no"/> <object class="GtkGridView" id="playlist_grid">
</object> <property name="orientation">vertical</property>
<packing> <property name="single-click-activate">true</property>
<property name="pack-type">end</property> <signal name="activate" handler="on_playlist_grid_clicked"/>
<property name="position">1</property> <style>
</packing> <class name="no-bg"/>
</child> </style>
<child> <property name="factory">
<object class="GtkButton"> <object class="GtkBuilderListItemFactory">
<property name="label" translatable="yes">play</property> <property name="bytes">
<property name="visible">True</property> <![CDATA[
<property name="can-focus">True</property> <?xml version="1.0" encoding="UTF-8"?>
<property name="receives-default">True</property> <interface>
<signal name="clicked" handler="on_standalone_play_clicked" swapped="no"/> <template class="GtkListItem">
</object> <property name="activatable">true</property>
<packing> <property name="child">
<property name="pack-type">end</property> <object class="GtkBox">
<property name="position">0</property> <property name="orientation">vertical</property>
</packing> <child>
</child> <object class="GtkPicture">
</object> <property name="content-fit">contain</property>
<packing> <property name="can-shrink">false</property>
<property name="expand">False</property> <binding name="tooltip-markup">
<property name="fill">True</property> <lookup name="tooltip" type="GridItem">
<property name="position">1</property> <lookup name="item">GtkListItem</lookup>
</packing> </lookup>
</binding>
<binding name="paintable">
<lookup name="cover" type="GridItem">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</child>
</object>
</property>
</template>
</interface>
]]>
</property>
</object>
</property>
</object>
</child>
<style>
<class name="no-bg"/>
</style>
</object>
</child>
<child>
<object class="GtkRevealer" id="actionbar_revealer">
<property name="transition-type">slide-up</property>
<child>
<object class="GtkActionBar" id="actionbar">
<child type="end">
<object class="GtkButton">
<property name="label" translatable="yes">cancel</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_selection_cancel_clicked" swapped="no"/>
</object>
</child>
<child type="end">
<object class="GtkButton">
<property name="label" translatable="yes">remove</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_selection_remove_clicked" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox" id="panel_standalone">
<property name="orientation">vertical</property>
<child>
<object class="GtkStack" id="standalone_stack">
<property name="vexpand">true</property>
<child>
<object class="GtkSpinner" id="standalone_spinner">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="standalone_scroll">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="kinetic-scrolling">False</property>
<property name="overlay-scrolling">False</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkImage" id="standalone_image">
<property name="icon-name">gtk-missing-image</property>
<property name="icon-size">large</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkActionBar" id="actionbar_standalone">
<child type="end">
<object class="GtkButton">
<property name="label" translatable="yes">play</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_standalone_play_clicked" swapped="no"/>
</object>
</child>
<child type="end">
<object class="GtkButton">
<property name="label" translatable="yes">remove</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="on_standalone_remove_clicked" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child> </child>
</object> </template>
<packing>
<property name="name">page1</property>
<property name="title">page1</property>
<property name="position">1</property>
</packing>
</child>
</template>
</interface> </interface>

View file

@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<template class="McgPlaylistToolbar" parent="GtkButtonBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">6</property>
<property name="layout-style">end</property>
<child>
<object class="GtkToggleButton" id="select_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Select multiple albums</property>
<signal name="toggled" handler="on_select_toggled" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">object-select-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
<property name="non-homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkButton" id="playlist_clear_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Clear the playlist</property>
<signal name="clicked" handler="on_clear_clicked" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">edit-clear</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
<property name="non-homogeneous">True</property>
</packing>
</child>
</template>
</interface>

View file

@ -1,547 +1,326 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.16"/> <requires lib="gtk+" version="4.8"/>
<template class="McgServerPanel" parent="GtkBox"> <requires lib="adw" version="1.2" />
<property name="visible">True</property> <object class="GtkBox" id="toolbar">
<property name="can-focus">False</property> <property name="orientation">horizontal</property>
<child> <property name="halign">end</property>
<object class="GtkStackSidebar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="stack">stack</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkStack" id="stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child> <child>
<object class="GtkBox"> <object class="GtkToggleButton" id="sidebar_switcher">
<property name="visible">True</property> <property name="icon-name">sidebar-show-symbolic</property>
<property name="can-focus">False</property> <property name="active">true</property>
<child type="center"> <property name="visible" bind-source="server_flap" bind-property="folded" bind-flags="sync-create"/>
<object class="GtkBox"> </object>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child type="center">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">dialog-information-symbolic</property>
<property name="icon_size">6</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<!-- n-columns=3 n-rows=4 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-spacing">2</property>
<property name="column-spacing">5</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">File:</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Audio:</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Bitrate:</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Error:</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="status_bitrate">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">&lt;i&gt;none&lt;/i&gt;</property>
<property name="use-markup">True</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="status_audio">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">&lt;i&gt;none&lt;/i&gt;</property>
<property name="use-markup">True</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="status_file">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">&lt;i&gt;none&lt;/i&gt;</property>
<property name="use-markup">True</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="status_error">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">&lt;i&gt;none&lt;/i&gt;</property>
<property name="use-markup">True</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="name">page0</property>
<property name="title" translatable="yes">Status</property>
</packing>
</child> </child>
</object>
<template class="McgServerPanel" parent="AdwBin">
<child> <child>
<object class="GtkBox"> <object class="AdwFlap" id="server_flap">
<property name="visible">True</property> <property name="flap-position">end</property>
<property name="can-focus">False</property> <property name="reveal-flap" bind-source="sidebar_switcher" bind-property="active" bind-flags="sync-create|bidirectional" />
<child type="center"> <property name="flap">
<object class="GtkBox"> <object class="GtkStackSidebar">
<property name="visible">True</property> <property name="stack">stack</property>
<property name="can-focus">False</property> <property name="name">server_stack_sidebar</property>
<property name="orientation">vertical</property> </object>
<child type="center"> </property>
<object class="GtkBox"> <property name="separator">
<property name="visible">True</property> <object class="GtkSeparator"/>
<property name="can-focus">False</property> </property>
<property name="orientation">vertical</property> <property name="content">
<property name="spacing">10</property> <object class="GtkStack" id="stack">
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">starred-symbolic</property>
<property name="icon_size">6</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<!-- n-columns=3 n-rows=7 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-spacing">2</property>
<property name="column-spacing">5</property>
<child> <child>
<object class="GtkLabel" id="stats_artists"> <object class="GtkStackPage">
<property name="visible">True</property> <property name="name">status</property>
<property name="can-focus">False</property> <property name="title">Status</property>
<property name="halign">end</property> <property name="child">
<property name="justify">right</property> <object class="AdwStatusPage">
<property name="selectable">True</property> <property name="icon-name">dialog-information-symbolic</property>
</object> <child>
<packing> <object class="GtkGrid">
<property name="left-attach">0</property> <property name="row-spacing">2</property>
<property name="top-attach">0</property> <property name="column-spacing">5</property>
</packing> <property name="hexpand">false</property>
<property name="halign">center</property>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">File:</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Audio:</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Bitrate:</property>
<layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Error:</property>
<layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="status_file">
<property name="halign">start</property>
<property name="label" translatable="yes">&lt;i&gt;none&lt;/i&gt;</property>
<property name="use-markup">True</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="status_audio">
<property name="halign">start</property>
<property name="label" translatable="yes">&lt;i&gt;none&lt;/i&gt;</property>
<property name="use-markup">True</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="status_bitrate">
<property name="halign">start</property>
<property name="label" translatable="yes">&lt;i&gt;none&lt;/i&gt;</property>
<property name="use-markup">True</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
<layout>
<property name="column">1</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="status_error">
<property name="halign">start</property>
<property name="label" translatable="yes">&lt;i&gt;none&lt;/i&gt;</property>
<property name="use-markup">True</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
<layout>
<property name="column">1</property>
<property name="row">3</property>
</layout>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child> </child>
<child> <child>
<object class="GtkLabel" id="stats_albums"> <object class="GtkStackPage">
<property name="visible">True</property> <property name="name">stats</property>
<property name="can-focus">False</property> <property name="title">Statistics</property>
<property name="halign">end</property> <property name="child">
<property name="justify">right</property> <object class="AdwStatusPage">
</object> <property name="icon-name">starred-symbolic</property>
<packing> <child>
<property name="left-attach">0</property> <object class="GtkGrid">
<property name="top-attach">1</property> <property name="row-spacing">2</property>
</packing> <property name="column-spacing">5</property>
<property name="column-homogeneous">true</property>
<property name="hexpand">false</property>
<property name="halign">center</property>
<child>
<object class="GtkLabel" id="stats_artists">
<property name="halign">end</property>
<property name="justify">right</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Artists</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="stats_albums">
<property name="halign">end</property>
<property name="justify">right</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Albums</property>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="stats_songs">
<property name="halign">end</property>
<property name="justify">right</property>
<layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Songs</property>
<layout>
<property name="column">1</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="stats_dbplaytime">
<property name="halign">end</property>
<property name="justify">right</property>
<layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Seconds</property>
<layout>
<property name="column">1</property>
<property name="row">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<layout>
<property name="column">0</property>
<property name="row">4</property>
<property name="column-span">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="stats_playtime">
<property name="halign">end</property>
<property name="justify">right</property>
<layout>
<property name="column">0</property>
<property name="row">5</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Seconds played</property>
<layout>
<property name="column">1</property>
<property name="row">5</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="stats_uptime">
<property name="halign">end</property>
<property name="justify">right</property>
<layout>
<property name="column">0</property>
<property name="row">6</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Seconds running</property>
<layout>
<property name="column">1</property>
<property name="row">6</property>
</layout>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child> </child>
<child> <child>
<object class="GtkLabel" id="stats_songs"> <object class="GtkStackPage">
<property name="visible">True</property> <property name="name">devices</property>
<property name="can-focus">False</property> <property name="title">Audio Devices</property>
<property name="halign">end</property> <property name="child">
<property name="justify">right</property> <object class="AdwStatusPage">
</object> <property name="icon-name">audio-speakers-symbolic</property>
<packing> <child>
<property name="left-attach">0</property> <object class="GtkListBox" id="output_devices">
<property name="top-attach">2</property> <property name="hexpand">false</property>
</packing> <property name="halign">center</property>
<property name="selection-mode">none</property>
<style>
<class name="no-bg"/>
</style>
</object>
</child>
</object>
</property>
</object>
</child> </child>
<child> </object>
<object class="GtkLabel"> </property>
<property name="visible">True</property> </object>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Albums</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Songs</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Artists</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="stats_dbplaytime">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">end</property>
<property name="justify">right</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Seconds</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="stats_uptime">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">end</property>
<property name="justify">right</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="stats_playtime">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">end</property>
<property name="justify">right</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Seconds played</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Seconds running</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">6</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="name">page1</property>
<property name="title" translatable="yes">Statistics</property>
<property name="position">1</property>
</packing>
</child> </child>
<child> </template>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child type="center">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child type="center">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">audio-speakers-symbolic</property>
<property name="icon_size">6</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkListBox" id="output_devices">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="selection-mode">none</property>
<style>
<class name="no-bg"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="name">page2</property>
<property name="title" translatable="yes">Audio Devices</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</template>
</interface> </interface>

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<template class="McgServerToolbar" parent="GtkButtonBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="layout-style">start</property>
<child>
<placeholder/>
</child>
</template>
</interface>

View file

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 --> <!-- Generated with glade 3.38.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.18"/> <requires lib="gtk+" version="4.8"/>
<requires lib="adw" version="1.2" />
<template class="McgShortcutsDialog" parent="GtkShortcutsWindow"> <template class="McgShortcutsDialog" parent="GtkShortcutsWindow">
<property name="modal">True</property> <property name="modal">1</property>
<child> <child>
<object class="GtkShortcutsSection"> <object class="GtkShortcutsSection">
<property name="visible">1</property> <property name="visible">1</property>
@ -11,53 +12,45 @@
<property name="max-height">10</property> <property name="max-height">10</property>
<child> <child>
<object class="GtkShortcutsGroup"> <object class="GtkShortcutsGroup">
<property name="visible">1</property>
<property name="title" translatable="yes">General</property> <property name="title" translatable="yes">General</property>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;KP_1</property> <property name="accelerator">&lt;primary&gt;KP_1</property>
<property name="title" translatable="yes">Switch to the Connection panel</property> <property name="title" translatable="yes">Switch to the Connection panel</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;KP_2</property> <property name="accelerator">&lt;primary&gt;KP_2</property>
<property name="title" translatable="yes">Switch to the Cover panel</property> <property name="title" translatable="yes">Switch to the Cover panel</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;KP_3</property> <property name="accelerator">&lt;primary&gt;KP_3</property>
<property name="title" translatable="yes">Switch to the Playlist panel</property> <property name="title" translatable="yes">Switch to the Playlist panel</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;KP_4</property> <property name="accelerator">&lt;primary&gt;KP_4</property>
<property name="title" translatable="yes">Switch to the Library panel</property> <property name="title" translatable="yes">Switch to the Library panel</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;k</property> <property name="accelerator">&lt;primary&gt;k</property>
<property name="title" translatable="yes">Show the keyboard shortcuts</property> <property name="title" translatable="yes">Show the keyboard shortcuts</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;i</property> <property name="accelerator">&lt;primary&gt;i</property>
<property name="title" translatable="yes">Open the info dialog</property> <property name="title" translatable="yes">Open the info dialog</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;q</property> <property name="accelerator">&lt;primary&gt;q</property>
<property name="title" translatable="yes">Quit the application</property> <property name="title" translatable="yes">Quit the application</property>
</object> </object>
@ -66,25 +59,21 @@
</child> </child>
<child> <child>
<object class="GtkShortcutsGroup"> <object class="GtkShortcutsGroup">
<property name="visible">1</property>
<property name="title" translatable="yes">Player</property> <property name="title" translatable="yes">Player</property>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;c</property> <property name="accelerator">&lt;primary&gt;c</property>
<property name="title" translatable="yes">Connect or disconnect</property> <property name="title" translatable="yes">Connect or disconnect</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;p</property> <property name="accelerator">&lt;primary&gt;p</property>
<property name="title" translatable="yes">Switch between play and pause</property> <property name="title" translatable="yes">Switch between play and pause</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;r</property> <property name="accelerator">&lt;primary&gt;r</property>
<property name="title" translatable="yes">Clear the playlist</property> <property name="title" translatable="yes">Clear the playlist</property>
</object> </object>
@ -93,11 +82,9 @@
</child> </child>
<child> <child>
<object class="GtkShortcutsGroup"> <object class="GtkShortcutsGroup">
<property name="visible">1</property>
<property name="title" translatable="yes">Cover Panel</property> <property name="title" translatable="yes">Cover Panel</property>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">F11</property> <property name="accelerator">F11</property>
<property name="title" translatable="yes">Show the cover in fullscreen mode</property> <property name="title" translatable="yes">Show the cover in fullscreen mode</property>
</object> </object>
@ -106,11 +93,9 @@
</child> </child>
<child> <child>
<object class="GtkShortcutsGroup"> <object class="GtkShortcutsGroup">
<property name="visible">1</property>
<property name="title" translatable="yes">Library Panel</property> <property name="title" translatable="yes">Library Panel</property>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">1</property>
<property name="accelerator">&lt;primary&gt;f</property> <property name="accelerator">&lt;primary&gt;f</property>
<property name="title" translatable="yes">Search the library</property> <property name="title" translatable="yes">Search the library</property>
</object> </object>

View file

@ -1,229 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.20"/> <requires lib="gtk+" version="4.8" />
<template class="McgAppWindow" parent="GtkApplicationWindow"> <requires lib="adw" version="1.2" />
<property name="can-focus">False</property> <template class="McgAppWindow" parent="AdwApplicationWindow">
<property name="icon-name">xyz.suruatoel.mcg</property> <property name="content">
<signal name="size-allocate" handler="on_resize" swapped="no"/> <object class="AdwToolbarView" id="toolbar_view">
<signal name="window-state-event" handler="on_state" swapped="no"/> <child type="top">
<child> <object class="AdwHeaderBar" id="headerbar">
<object class="GtkOverlay"> <property name="centering-policy">strict</property>
<property name="visible">True</property> <property name="show_end_title_buttons">true</property>
<property name="can-focus">False</property> <property name="title-widget">
<child> <object class="AdwViewSwitcherTitle" id="headerbar_panel_switcher">
<object class="GtkStack" id="content_stack"> <property name="title">CoverGrid</property>
<property name="visible">True</property> <property name="stack">panel_stack</property>
<property name="can-focus">False</property> </object>
<property name="transition-duration">100</property> </property>
<property name="transition-type">crossfade</property> <child>
<child> <object class="GtkSwitch" id="headerbar_button_connect">
<object class="GtkStack" id="panel_stack"> <signal name="state-set" handler="on_headerbar_connection_state_set" swapped="no"/>
<property name="visible">True</property> </object>
<property name="can-focus">False</property> </child>
<child> <child>
<placeholder/> <object class="GtkToggleButton" id="headerbar_button_playpause">
<property name="tooltip-text" translatable="yes">Switch between play and pause</property>
<signal name="toggled" handler="on_headerbar_playpause_toggled" swapped="no"/>
<child>
<object class="GtkImage">
<property name="can-focus">False</property>
<property name="icon-name">media-playback-start</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkVolumeButton" id="headerbar_button_volume">
<signal name="value-changed" handler="on_headerbar_volume_changed" swapped="no"/>
</object>
</child>
<child type="end">
<object class="GtkStack" id="toolbar_stack">
</object>
</child>
</object>
</child> </child>
</object> <property name="content">
<packing> <object class="GtkBox" id="content_box">
<property name="name">page0</property> <property name="orientation">vertical</property>
<property name="title">page0</property> <child>
</packing> <object class="AdwToastOverlay" id="info_toast">
</child> <child>
</object> <object class="GtkStack" id="content_stack">
<packing> <property name="name">content_stack</property>
<property name="index">-1</property> <property name="vexpand">true</property>
</packing> <child>
</child> <object class="AdwViewStack" id="panel_stack">
<child type="overlay"> <property name="vexpand">true</property>
<object class="GtkRevealer" id="info_revealer"> <signal name="notify::visible-child" handler="on_stack_switched" swapped="no"/>
<property name="visible">True</property> </object>
<property name="can-focus">False</property> </child>
<property name="valign">start</property> </object>
<child> </child>
<object class="GtkInfoBar" id="info_bar"> </object>
<property name="can-focus">False</property> </child>
<property name="valign">start</property> <child>
<property name="margin-start">10</property> <object class="AdwViewSwitcherBar">
<property name="margin-end">10</property> <property name="stack">panel_stack</property>
<property name="orientation">vertical</property> <binding name="reveal">
<property name="baseline-position">top</property> <lookup name="title-visible">headerbar_panel_switcher</lookup>
<property name="show-close-button">True</property> </binding>
<signal name="close" handler="on_info_bar_close" swapped="no"/> </object>
<signal name="response" handler="on_info_bar_response" swapped="no"/> </child>
<child internal-child="action_area"> </object>
<object class="GtkButtonBox"> </property>
<property name="can-focus">False</property> </object>
</object> </property>
<packing> </template>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child internal-child="content_area">
<object class="GtkBox">
<property name="can-focus">False</property>
<child>
<object class="GtkLabel" id="info_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child type="titlebar">
<object class="GtkHeaderBar" id="headerbar">
<property name="name">headerbar</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="show-close-button">True</property>
<child type="title">
<object class="GtkStack" id="headerbar_title_stack">
<property name="name">headerbar-connection</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="transition-duration">100</property>
<property name="transition-type">crossfade</property>
<child>
<object class="GtkLabel" id="headerbar_connection_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Connect to MPD</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="name">page1</property>
<property name="title">page1</property>
</packing>
</child>
<child>
<object class="GtkStackSwitcher" id="headerbar_panel_switcher">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="stack">panel_stack</property>
</object>
<packing>
<property name="name">page0</property>
<property name="title">page0</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkSwitch" id="headerbar_button_connect">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">Connect or disconnect</property>
<signal name="notify::active" handler="on_headerbar_connection_active_notify" swapped="no"/>
<signal name="state-set" handler="on_headerbar_connection_state_set" swapped="no"/>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="headerbar_button_playpause">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Switch between play and pause</property>
<signal name="toggled" handler="on_headerbar_playpause_toggled" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">media-playback-start</property>
</object>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkVolumeButton" id="headerbar_button_volume">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Adjust the volume</property>
<property name="relief">none</property>
<property name="orientation">vertical</property>
<signal name="button-press-event" handler="on_headerbar_volume_press" swapped="no"/>
<signal name="button-release-event" handler="on_headerbar_volume_release" swapped="no"/>
<signal name="value-changed" handler="on_headerbar_volume_changed" swapped="no"/>
<child internal-child="plus_button">
<object class="GtkButton">
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="relief">none</property>
</object>
</child>
<child internal-child="minus_button">
<object class="GtkButton">
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="relief">none</property>
</object>
</child>
</object>
<packing>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkStack" id="toolbar_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="pack-type">end</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
<style>
<class name="bg-texture"/>
</style>
</template>
</interface> </interface>

View file

@ -4,18 +4,15 @@
<file>gtk.css</file> <file>gtk.css</file>
<file>noise-texture.png</file> <file>noise-texture.png</file>
<file>ui/window.ui</file> <file>ui/window.ui</file>
<!--
<file>ui/gtk.menu.ui</file> <file>ui/gtk.menu.ui</file>
<file>ui/info-dialog.ui</file> -->
<file>ui/shortcuts-dialog.ui</file> <file>ui/shortcuts-dialog.ui</file>
<file>ui/connection-panel.ui</file> <file>ui/connection-panel.ui</file>
<file>ui/album-headerbar.ui</file> <file>ui/album-headerbar.ui</file>
<file>ui/server-toolbar.ui</file>
<file>ui/server-panel.ui</file> <file>ui/server-panel.ui</file>
<file>ui/cover-toolbar.ui</file>
<file>ui/cover-panel.ui</file> <file>ui/cover-panel.ui</file>
<file>ui/playlist-toolbar.ui</file>
<file>ui/playlist-panel.ui</file> <file>ui/playlist-panel.ui</file>
<file>ui/library-toolbar.ui</file>
<file>ui/library-panel.ui</file> <file>ui/library-panel.ui</file>
</gresource> </gresource>
</gresources> </gresources>

View file

@ -4,6 +4,7 @@
<value nick="artist" value="0" /> <value nick="artist" value="0" />
<value nick="title" value="1" /> <value nick="title" value="1" />
<value nick="year" value="2" /> <value nick="year" value="2" />
<value nick="modified" value="3" />
</enum> </enum>
<schema path="/xyz/suruatoel/mcg/" id="xyz.suruatoel.mcg" gettext-domain="mcg"> <schema path="/xyz/suruatoel/mcg/" id="xyz.suruatoel.mcg" gettext-domain="mcg">
<key type="s" name="host"> <key type="s" name="host">

View file

@ -1,5 +1,5 @@
project('mcg', project('mcg',
version: '3.1', version: '3.2.1',
meson_version: '>= 0.59.0', meson_version: '>= 0.59.0',
default_options: [ default_options: [
'warning_level=2', 'warning_level=2',

BIN
po/de.mo

Binary file not shown.

View file

@ -2,17 +2,17 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: CoverGrid (mcg)\n" "Project-Id-Version: CoverGrid (mcg)\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-09-05 15:08+0200\n" "POT-Creation-Date: 2023-01-08 19:06+0100\n"
"PO-Revision-Date: 2020-10-24 14:41+0200\n" "PO-Revision-Date: 2023-01-08 19:07+0100\n"
"Last-Translator: coderkun <olli@suruatoel.xyz>\n" "Last-Translator: coderkun <olli@suruatoel.xyz>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: de_DE\n" "Language: de_DE\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.4.1\n"
"X-Poedit-Basepath: ../../..\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.2.2\n"
"X-Poedit-Basepath: ../../..\n"
"X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-SourceCharset: UTF-8\n"
#: data/xyz.suruatoel.mcg.gschema.xml:11 #: data/xyz.suruatoel.mcg.gschema.xml:11
@ -216,15 +216,19 @@ msgstr "nach Titel"
msgid "sort by year" msgid "sort by year"
msgstr "nach Jahr" msgstr "nach Jahr"
#: data/ui/library-toolbar.ui:169 data/ui/shortcuts-dialog.ui:115 #: data/ui/library-toolbar.ui:134
msgid "sort by modification"
msgstr "nach Änderungsdatum"
#: data/ui/library-toolbar.ui:185 data/ui/shortcuts-dialog.ui:115
msgid "Search the library" msgid "Search the library"
msgstr "Die Bibliothek durchsuchen" msgstr "Die Bibliothek durchsuchen"
#: data/ui/library-toolbar.ui:192 data/ui/playlist-toolbar.ui:15 #: data/ui/library-toolbar.ui:208 data/ui/playlist-toolbar.ui:15
msgid "Select multiple albums" msgid "Select multiple albums"
msgstr "Mehrere Alben auswählen" msgstr "Mehrere Alben auswählen"
#: data/ui/library-toolbar.ui:214 #: data/ui/library-toolbar.ui:230
msgid "Settings and actions" msgid "Settings and actions"
msgstr "Einstellungen und Aktionen" msgstr "Einstellungen und Aktionen"
@ -353,11 +357,11 @@ msgstr "Zu MPD verbinden"
msgid "Adjust the volume" msgid "Adjust the volume"
msgstr "Die Lautstärke anpassen" msgstr "Die Lautstärke anpassen"
#: src/librarypanel.py:419 #: src/librarypanel.py:421
msgid "Loading albums" msgid "Loading albums"
msgstr "Alben werden geladen" msgstr "Alben werden geladen"
#: src/librarypanel.py:519 #: src/librarypanel.py:521
msgid "Loading images" msgid "Loading images"
msgstr "Bilder werden geladen" msgstr "Bilder werden geladen"

BIN
po/en.mo

Binary file not shown.

View file

@ -2,17 +2,17 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: CoverGrid (mcg)\n" "Project-Id-Version: CoverGrid (mcg)\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-09-05 15:08+0200\n" "POT-Creation-Date: 2023-01-08 19:06+0100\n"
"PO-Revision-Date: 2020-10-24 14:40+0200\n" "PO-Revision-Date: 2023-01-08 19:07+0100\n"
"Last-Translator: coderkun <olli@suruatoel.xyz>\n" "Last-Translator: coderkun <olli@suruatoel.xyz>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: en\n" "Language: en\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.4.1\n"
"X-Poedit-Basepath: ../../..\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.2.2\n"
"X-Poedit-Basepath: ../../..\n"
"X-Poedit-SearchPath-0: mcg\n" "X-Poedit-SearchPath-0: mcg\n"
"X-Poedit-SearchPath-1: data/ui\n" "X-Poedit-SearchPath-1: data/ui\n"
@ -217,15 +217,19 @@ msgstr "by Title"
msgid "sort by year" msgid "sort by year"
msgstr "by Year" msgstr "by Year"
#: data/ui/library-toolbar.ui:169 data/ui/shortcuts-dialog.ui:115 #: data/ui/library-toolbar.ui:134
msgid "sort by modification"
msgstr "by Modification Date"
#: data/ui/library-toolbar.ui:185 data/ui/shortcuts-dialog.ui:115
msgid "Search the library" msgid "Search the library"
msgstr "Search the library" msgstr "Search the library"
#: data/ui/library-toolbar.ui:192 data/ui/playlist-toolbar.ui:15 #: data/ui/library-toolbar.ui:208 data/ui/playlist-toolbar.ui:15
msgid "Select multiple albums" msgid "Select multiple albums"
msgstr "Select multiple albums" msgstr "Select multiple albums"
#: data/ui/library-toolbar.ui:214 #: data/ui/library-toolbar.ui:230
msgid "Settings and actions" msgid "Settings and actions"
msgstr "Settings and actions" msgstr "Settings and actions"
@ -354,11 +358,11 @@ msgstr "Connect to MPD"
msgid "Adjust the volume" msgid "Adjust the volume"
msgstr "Adjust the volume" msgstr "Adjust the volume"
#: src/librarypanel.py:419 #: src/librarypanel.py:421
msgid "Loading albums" msgid "Loading albums"
msgstr "Loading albums" msgstr "Loading albums"
#: src/librarypanel.py:519 #: src/librarypanel.py:521
msgid "Loading images" msgid "Loading images"
msgstr "Loading images" msgstr "Loading images"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: mcg\n" "Project-Id-Version: mcg\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-09-05 15:08+0200\n" "POT-Creation-Date: 2023-01-08 19:06+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -215,15 +215,19 @@ msgstr ""
msgid "sort by year" msgid "sort by year"
msgstr "" msgstr ""
#: data/ui/library-toolbar.ui:169 data/ui/shortcuts-dialog.ui:115 #: data/ui/library-toolbar.ui:134
msgid "sort by modification"
msgstr ""
#: data/ui/library-toolbar.ui:185 data/ui/shortcuts-dialog.ui:115
msgid "Search the library" msgid "Search the library"
msgstr "" msgstr ""
#: data/ui/library-toolbar.ui:192 data/ui/playlist-toolbar.ui:15 #: data/ui/library-toolbar.ui:208 data/ui/playlist-toolbar.ui:15
msgid "Select multiple albums" msgid "Select multiple albums"
msgstr "" msgstr ""
#: data/ui/library-toolbar.ui:214 #: data/ui/library-toolbar.ui:230
msgid "Settings and actions" msgid "Settings and actions"
msgstr "" msgstr ""
@ -352,11 +356,11 @@ msgstr ""
msgid "Adjust the volume" msgid "Adjust the volume"
msgstr "" msgstr ""
#: src/librarypanel.py:419 #: src/librarypanel.py:421
msgid "Loading albums" msgid "Loading albums"
msgstr "" msgstr ""
#: src/librarypanel.py:519 #: src/librarypanel.py:521
msgid "Loading images" msgid "Loading images"
msgstr "" msgstr ""

View file

@ -2,15 +2,16 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, GObject from gi.repository import Gtk, GObject, Adw
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/album-headerbar.ui') @Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/album-headerbar.ui')
class AlbumHeaderbar(Gtk.HeaderBar): class AlbumHeaderbar(Adw.Bin):
__gtype_name__ = 'McgAlbumHeaderbar' __gtype_name__ = 'McgAlbumHeaderbar'
__gsignals__ = { __gsignals__ = {
'close': (GObject.SIGNAL_RUN_FIRST, None, ()) 'close': (GObject.SIGNAL_RUN_FIRST, None, ())

View file

@ -5,11 +5,12 @@ import logging
import urllib import urllib
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '4.0')
from gi.repository import Gio, Gtk, Gdk, GLib gi.require_version('Adw', '1')
from gi.repository import Gio, Gtk, Gdk, GLib, Adw
from .window import Window from .window import Window
from .infodialog import InfoDialog #from .infodialog import InfoDialog
@ -25,9 +26,18 @@ class Application(Gtk.Application):
self._window = None self._window = None
self._info_dialog = None self._info_dialog = None
self._verbosity = logging.WARNING self._verbosity = logging.WARNING
#self.create_action('quit', self.quit, ['<primary>q']) self.set_accels_for_action('window.close', ['<primary>q'])
#self.create_action('about', self.on_about_action) self.set_accels_for_action('win.show-help-overlay', ['<primary>k'])
#self.create_action('preferences', self.on_preferences_action) self.set_accels_for_action('app.info', ['<primary>i'])
self.set_accels_for_action('win.connect', ['<primary>c'])
self.set_accels_for_action('win.play', ['<primary>p'])
self.set_accels_for_action('win.clear-playlist', ['<primary>r'])
self.set_accels_for_action('win.toggle-fullscreen', ['F11'])
self.set_accels_for_action('win.search-library', ['<primary>f'])
self.set_accels_for_action('win.panel("0")', ['<primary>KP_1'])
self.set_accels_for_action('win.panel("1")', ['<primary>KP_2'])
self.set_accels_for_action('win.panel("2")', ['<primary>KP_3'])
self.set_accels_for_action('win.panel("3")', ['<primary>KP_4'])
def do_startup(self): def do_startup(self):
@ -37,7 +47,7 @@ class Application(Gtk.Application):
self._set_default_settings() self._set_default_settings()
self._load_css() self._load_css()
self._setup_actions() self._setup_actions()
self._load_appmenu() self._setup_adw()
def do_activate(self): def do_activate(self):
@ -48,10 +58,15 @@ class Application(Gtk.Application):
def on_menu_info(self, action, value): def on_menu_info(self, action, value):
if not self._info_dialog: self._info_dialog = Adw.AboutDialog()
self._info_dialog = InfoDialog() self._info_dialog.set_application_icon("xyz.suruatoel.mcg")
self._info_dialog.run() self._info_dialog.set_application_name("CoverGrid")
self._info_dialog.hide() self._info_dialog.set_version("3.2.1")
self._info_dialog.set_comments("CoverGrid is a client for the Music Player Daemon, focusing on albums instead of single tracks.")
self._info_dialog.set_website("https://www.suruatoel.xyz/codes/mcg")
self._info_dialog.set_license_type(Gtk.License.GPL_3_0)
self._info_dialog.set_issue_url("https://git.suruatoel.xyz/coderkun/mcg")
self._info_dialog.present()
def on_menu_quit(self, action, value): def on_menu_quit(self, action, value):
@ -70,15 +85,15 @@ class Application(Gtk.Application):
def _set_default_settings(self): def _set_default_settings(self):
settings = Gtk.Settings.get_default() style_manager = Adw.StyleManager.get_default()
settings.set_property('gtk-application-prefer-dark-theme', True) style_manager.set_color_scheme(Adw.ColorScheme.PREFER_DARK)
def _load_css(self): def _load_css(self):
styleProvider = Gtk.CssProvider() styleProvider = Gtk.CssProvider()
styleProvider.load_from_resource(self._get_resource_path('gtk.css')) styleProvider.load_from_resource(self._get_resource_path('gtk.css'))
Gtk.StyleContext.add_provider_for_screen( Gtk.StyleContext.add_provider_for_display(
Gdk.Screen.get_default(), Gdk.Display.get_default(),
styleProvider, styleProvider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
) )
@ -93,12 +108,18 @@ class Application(Gtk.Application):
self.add_action(action) self.add_action(action)
def _load_appmenu(self):
builder = Gtk.Builder()
builder.set_translation_domain(Application.DOMAIN)
builder.add_from_resource(self._get_resource_path('ui/gtk.menu.ui'))
self.set_app_menu(builder.get_object('app-menu'))
def _get_resource_path(self, path): def _get_resource_path(self, path):
return "/{}/{}".format(Application.ID.replace('.', '/'), path) return "/{}/{}".format(Application.ID.replace('.', '/'), path)
def _setup_adw(self):
Adw.HeaderBar()
Adw.ToolbarView()
Adw.ViewSwitcherTitle()
Adw.ViewSwitcherBar()
Adw.ViewStackPage()
Adw.ToastOverlay()
Adw.StatusPage()
Adw.Flap()
Adw.EntryRow()
Adw.PasswordEntryRow()

View file

@ -3,6 +3,7 @@
import concurrent.futures import concurrent.futures
import configparser import configparser
import dateutil.parser
import logging import logging
import os import os
import queue import queue
@ -649,57 +650,25 @@ class Client(Base):
def _get_albumart(self, album): def _get_albumart(self, album):
data = None
if album in self._albums: if album in self._albums:
album = self._albums[album] album = self._albums[album]
if not album.get_tracks():
return None
self._logger.debug("get albumart for album \"%s\"", album.get_title()) self._logger.debug("get albumart for album \"%s\"", album.get_title())
size = 1
offset = 0
index = 0
# Read data until size is reached # Use "albumart" command
try: if album.get_tracks():
while offset < size: try:
self._write('albumart', args=[album.get_tracks()[0].get_file(), offset]) return (album, self._read_binary('albumart', album.get_tracks()[0].get_file(), False))
except CommandException as e:
# The "albumart" command throws an exception if not found
if e.get_error_number() != Client.PROTOCOL_ERROR_NOEXISTS:
raise e
# If no albumart can be found, use "readpicture" command
for track in album.get_tracks():
data = self._read_binary('readpicture', track.get_file(), True)
if data:
return (album, data)
# Read first line which tells us whether there is an albumart return (album, None)
line = self._read_line()
if line.startswith(Client.PROTOCOL_ERROR):
error = line[len(Client.PROTOCOL_ERROR):].strip()
self._logger.debug("command failed: %r", error)
raise CommandException(error)
# First line is the file size
size = int(self._parse_dict([line])['size'])
self._logger.debug("size: %d", size)
# Second line is the count of bytes read
binary = int(self._parse_dict([self._read_line()])['binary'])
self._logger.debug("binary: %d", binary)
# Create new data array on the first iteration
if not data:
data = bytearray(size)
# Create a view for the current chunk of data
data_view = memoryview(data)[offset:offset+binary]
# Read actual bytes
self._read_bytes(data_view, binary)
offset += binary
# Read line break to complete previous repsonse
self._read_line()
# Read command completion
end = self._read_line()
if not end.startswith(Client.PROTOCOL_COMPLETION):
self._logger.debug("albumart not completed")
data = None
break
except CommandException as e:
# If no albumart can be found, do not throw an exception
if e.get_error_number() == Client.PROTOCOL_ERROR_NOEXISTS:
data = None
else:
raise e
return (album, data)
def _get_custom(self, name): def _get_custom(self, name):
@ -843,6 +812,55 @@ class Client(Base):
return None return None
def _read_binary(self, command, filename, has_mimetype):
data = None
size = 1
offset = 0
index = 0
# Read data until size is reached
while offset < size:
self._write(command, args=[filename, offset])
# Read first line
line = self._read_line()
# Check first line for error
if line.startswith(Client.PROTOCOL_ERROR):
error = line[len(Client.PROTOCOL_ERROR):].strip()
self._logger.debug("command failed: %r", error)
raise CommandException(error)
# Check first line for completion
if line.startswith(Client.PROTOCOL_COMPLETION):
break
# First line is the file size
size = int(self._parse_dict([line])['size'])
self._logger.debug("size: %d", size)
# For some commands the second line is the mimetype
if has_mimetype:
mimetype = self._parse_dict([self._read_line()])['type']
# Next line is the count of bytes read
binary = int(self._parse_dict([self._read_line()])['binary'])
self._logger.debug("binary: %d", binary)
# Create new data array on the first iteration
if not data:
data = bytearray(size)
# Create a view for the current chunk of data
data_view = memoryview(data)[offset:offset+binary]
# Read actual bytes
self._read_bytes(data_view, binary)
offset += binary
# Read line break to complete previous repsonse
self._read_line()
# Read command completion
end = self._read_line()
if not end.startswith(Client.PROTOCOL_COMPLETION):
self._logger.debug("albumart not completed")
data = None
break
return data
def _read_bytes(self, buf, nbytes): def _read_bytes(self, buf, nbytes):
self._logger.debug("reading bytes") self._logger.debug("reading bytes")
# Use already buffered data # Use already buffered data
@ -937,6 +955,8 @@ class Client(Base):
track.set_date(song['date']) track.set_date(song['date'])
if 'albumartist' in song: if 'albumartist' in song:
track.set_albumartists(song['albumartist']) track.set_albumartists(song['albumartist'])
if 'last-modified' in song:
track.set_last_modified(song['last-modified'])
return track return track
@ -998,6 +1018,7 @@ class MCGAlbum:
self._host = host self._host = host
self._tracks = [] self._tracks = []
self._length = 0 self._length = 0
self._last_modified = None
self._id = Utils.generate_id(title) self._id = Utils.generate_id(title)
@ -1057,6 +1078,9 @@ class MCGAlbum:
path = os.path.dirname(track.get_file()) path = os.path.dirname(track.get_file())
if path not in self._pathes: if path not in self._pathes:
self._pathes.append(path) self._pathes.append(path)
if track.get_last_modified():
if not self._last_modified or track.get_last_modified() > self._last_modified:
self._last_modified = track.get_last_modified()
def get_tracks(self): def get_tracks(self):
@ -1067,6 +1091,10 @@ class MCGAlbum:
return self._length return self._length
def get_last_modified(self):
return self._last_modified
def filter(self, filter_string): def filter(self, filter_string):
if len(filter_string) == 0: if len(filter_string) == 0:
return True return True
@ -1093,7 +1121,7 @@ class MCGAlbum:
return True return True
def compare(album1, album2, criterion=None): def compare(album1, album2, criterion=None, reverse=False):
if criterion == None: if criterion == None:
criterion = SortOrder.TITLE criterion = SortOrder.TITLE
if criterion == SortOrder.ARTIST: if criterion == SortOrder.ARTIST:
@ -1102,21 +1130,25 @@ class MCGAlbum:
value_function = "get_title" value_function = "get_title"
elif criterion == SortOrder.YEAR: elif criterion == SortOrder.YEAR:
value_function = "get_date" value_function = "get_date"
elif criterion == SortOrder.MODIFIED:
value_function = "get_last_modified"
reverseMultiplier = -1 if reverse else 1
value1 = getattr(album1, value_function)() value1 = getattr(album1, value_function)()
value2 = getattr(album2, value_function)() value2 = getattr(album2, value_function)()
if value1 is None and value2 is None: if value1 is None and value2 is None:
return 0 return 0
elif value1 is None: elif value1 is None:
return -1 return -1 * reverseMultiplier
elif value2 is None: elif value2 is None:
return 1 return 1 * reverseMultiplier
if value1 < value2: if value1 < value2:
return -1 return -1 * reverseMultiplier
elif value1 == value2: elif value1 == value2:
return 0 return 0
else: else:
return 1 return 1 * reverseMultiplier
@ -1137,6 +1169,7 @@ class MCGTrack:
self._track = None self._track = None
self._length = 0 self._length = 0
self._date = None self._date = None
self._last_modified = None
def __eq__(self, other): def __eq__(self, other):
@ -1208,6 +1241,18 @@ class MCGTrack:
return self._file return self._file
def set_last_modified(self, date_string):
if date_string:
try:
self._last_modified = dateutil.parser.isoparse(date_string)
except ValueError as e:
self._logger.debug("Invalid date format: %s", date_string)
def get_last_modified(self):
return self._last_modified
class MCGPlaylistTrack(MCGTrack): class MCGPlaylistTrack(MCGTrack):

View file

@ -2,9 +2,11 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
import locale
from gi.repository import Gtk, GObject from gi.repository import Gtk, Gio, GObject, Adw
from mcg.zeroconf import ZeroconfProvider from mcg.zeroconf import ZeroconfProvider
@ -12,56 +14,58 @@ from mcg.zeroconf import ZeroconfProvider
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/connection-panel.ui') @Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/connection-panel.ui')
class ConnectionPanel(Gtk.Box): class ConnectionPanel(Adw.Bin):
__gtype_name__ = 'McgConnectionPanel' __gtype_name__ = 'McgConnectionPanel'
__gsignals__ = { __gsignals__ = {
'connection-changed': (GObject.SIGNAL_RUN_FIRST, None, (str, int, str)) 'connection-changed': (GObject.SIGNAL_RUN_FIRST, None, (str, int, str))
} }
# Widgets # Widgets
toolbar = Gtk.Template.Child()
zeroconf_list = Gtk.Template.Child() zeroconf_list = Gtk.Template.Child()
host_entry = Gtk.Template.Child() host_row = Gtk.Template.Child()
port_spinner = Gtk.Template.Child() port_spinner = Gtk.Template.Child()
password_entry = Gtk.Template.Child() password_row = Gtk.Template.Child()
def __init__(self): def __init__(self, **kwargs):
super().__init__() super().__init__(**kwargs)
self._services = Gtk.ListStore(str, str, int)
self._profile = None
# Zeroconf
self.zeroconf_list.set_model(self._services)
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Zeroconf", renderer, text=0)
self.zeroconf_list.append_column(column)
# Zeroconf provider # Zeroconf provider
self._zeroconf_provider = ZeroconfProvider() self._zeroconf_provider = ZeroconfProvider()
self._zeroconf_provider.connect_signal(ZeroconfProvider.SIGNAL_SERVICE_NEW, self.on_new_service) self._zeroconf_provider.connect_signal(ZeroconfProvider.SIGNAL_SERVICE_NEW, self.on_new_service)
def get_toolbar(self):
return self.toolbar
def set_selected(self, selected):
pass
def on_new_service(self, service): def on_new_service(self, service):
name, host, port = service name, host, port = service
self._services.append([name, host, port])
row_button = Gtk.Button()
row_button.set_label(locale.gettext("use"))
row_button.connect("clicked", self.on_service_selected, host, port)
row = Adw.ActionRow()
row.set_title(name)
row.set_subtitle("{} ({})".format(host, port))
row.add_suffix(row_button)
self.zeroconf_list.insert(row, -1)
def on_service_selected(self, widget, host, port):
self.set_host(host)
self.set_port(port)
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_service_selected(self, selection): def on_host_entry_apply(self, widget):
model, treeiter = selection.get_selected()
if treeiter != None:
service = model[treeiter]
self.set_host(service[1])
self.set_port(service[2])
@Gtk.Template.Callback()
def on_zeroconf_list_outfocused(self, widget, event):
self.zeroconf_list.get_selection().unselect_all()
@Gtk.Template.Callback()
def on_host_entry_outfocused(self, widget, event):
self._call_back() self._call_back()
@ -70,17 +74,12 @@ class ConnectionPanel(Gtk.Box):
self._call_back() self._call_back()
@Gtk.Template.Callback()
def on_password_entry_outfocused(self, widget, event):
self._call_back()
def set_host(self, host): def set_host(self, host):
self.host_entry.set_text(host) self.host_row.set_text(host)
def get_host(self): def get_host(self):
return self.host_entry.get_text() return self.host_row.get_text()
def set_port(self, port): def set_port(self, port):
@ -94,11 +93,11 @@ class ConnectionPanel(Gtk.Box):
def set_password(self, password): def set_password(self, password):
if password is None: if password is None:
password = "" password = ""
self.password_entry.set_text(password) self.password_row.set_text(password)
def get_password(self): def get_password(self):
if self.password_entry.get_text() == "": if self.password_row.get_text() == "":
return None return None
else: else:
return self.password_entry.get_text() return self.password_entry.get_text()

View file

@ -2,7 +2,7 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '4.0')
import logging import logging
import math import math
@ -13,27 +13,6 @@ from mcg.utils import Utils
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/cover-toolbar.ui')
class CoverToolbar(Gtk.ButtonBox):
__gtype_name__ = 'McgCoverToolbar'
__gsignals__ = {
'fullscreen': (GObject.SIGNAL_RUN_FIRST, None, ())
}
# Widgets
fullscreen_button = Gtk.Template.Child()
def __init__(self):
super().__init__()
def set_fullscreen_sensitive(self, sensitive):
self.fullscreen_button.set_sensitive(sensitive)
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/cover-panel.ui') @Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/cover-panel.ui')
class CoverPanel(Gtk.Overlay): class CoverPanel(Gtk.Overlay):
__gtype_name__ = 'McgCoverPanel' __gtype_name__ = 'McgCoverPanel'
@ -44,9 +23,13 @@ class CoverPanel(Gtk.Overlay):
} }
# Widgets # Widgets
# Toolbar
toolbar = Gtk.Template.Child()
fullscreen_button = Gtk.Template.Child()
# Cover # Cover
cover_stack = Gtk.Template.Child() cover_stack = Gtk.Template.Child()
cover_spinner = Gtk.Template.Child() cover_spinner = Gtk.Template.Child()
cover_default = Gtk.Template.Child()
cover_scroll = Gtk.Template.Child() cover_scroll = Gtk.Template.Child()
cover_box = Gtk.Template.Child() cover_box = Gtk.Template.Child()
cover_image = Gtk.Template.Child() cover_image = Gtk.Template.Child()
@ -61,57 +44,61 @@ class CoverPanel(Gtk.Overlay):
def __init__(self): def __init__(self, **kwargs):
super().__init__() super().__init__(**kwargs)
self._current_album = None self._current_album = None
self._current_cover_album = None self._current_cover_album = None
self._cover_pixbuf = None self._cover_pixbuf = None
self._timer = None self._timer = None
self._properties = {} self._properties = {}
self._icon_theme = Gtk.IconTheme.get_default() self._icon_theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default())
self._fullscreened = False self._fullscreened = False
self._is_selected = False
self._current_size = None self._current_size = None
self._cover_pixbuf = self._get_default_image() #self._cover_pixbuf = self._get_default_image()
# Widgets # Widgets
self._toolbar = CoverToolbar()
self.cover_stack.set_visible_child(self.cover_scroll) self.cover_stack.set_visible_child(self.cover_scroll)
# Initial actions # Initial actions
GObject.idle_add(self._enable_tracklist) GObject.idle_add(self._enable_tracklist)
# Click handler for image
clickController = Gtk.GestureClick()
clickController.connect('pressed', self.on_cover_box_pressed)
self.cover_box.add_controller(clickController)
# Button controller for songs scale
buttonController = Gtk.GestureClick()
buttonController.connect('pressed', self.on_songs_scale_pressed)
buttonController.connect('unpaired-release', self.on_songs_scale_released)
self.songs_scale.add_controller(buttonController)
def get_toolbar(self): def get_toolbar(self):
return self._toolbar return self.toolbar
def set_selected(self, selected): def set_selected(self, selected):
self._is_selected = selected pass
@Gtk.Template.Callback() def on_cover_box_pressed(self, widget, npress, x, y):
def on_cover_box_pressed(self, widget, event): if self._current_album and npress == 2:
if self._current_album and event.type == Gdk.EventType._2BUTTON_PRESS:
self.emit('toggle-fullscreen') self.emit('toggle-fullscreen')
@Gtk.Template.Callback() def set_width(self, width):
def on_cover_size_allocate(self, widget, allocation):
GObject.idle_add(self._resize_image) GObject.idle_add(self._resize_image)
self.cover_info_scroll.set_max_content_width(allocation.width // 2) self.cover_info_scroll.set_max_content_width(width // 2)
@Gtk.Template.Callback() def on_songs_scale_pressed(self, widget, npress, x, y):
def on_songs_start_change(self, widget, event):
if self._timer: if self._timer:
GObject.source_remove(self._timer) GObject.source_remove(self._timer)
self._timer = None self._timer = None
def on_songs_scale_released(self, widget, x, y, npress, sequence):
@Gtk.Template.Callback()
def on_songs_change(self, widget, event):
value = int(self.songs_scale.get_value()) value = int(self.songs_scale.get_value())
time = self._current_album.get_length() time = self._current_album.get_length()
tracks = self._current_album.get_tracks() tracks = self._current_album.get_tracks()
@ -144,7 +131,7 @@ class CoverPanel(Gtk.Overlay):
# Set current album # Set current album
self._current_album = album self._current_album = album
self._enable_tracklist() self._enable_tracklist()
self._toolbar.set_fullscreen_sensitive(self._current_album is not None) self.fullscreen_button.set_sensitive(self._current_album is not None)
def set_play(self, pos, time): def set_play(self, pos, time):
@ -168,13 +155,12 @@ class CoverPanel(Gtk.Overlay):
def set_fullscreen(self, active): def set_fullscreen(self, active):
if active: if active:
self.info_revealer.set_reveal_child(False) self.info_revealer.set_reveal_child(False)
self.cover_box.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA(0, 0, 0, 1))
GObject.idle_add(self._resize_image) GObject.idle_add(self._resize_image)
self._fullscreened = True self._fullscreened = True
else: else:
self._fullscreened = False self._fullscreened = False
self.info_revealer.set_reveal_child(True) if self._current_album:
self.cover_box.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA(0, 0, 0, 0)) self.info_revealer.set_reveal_child(True)
GObject.idle_add(self._resize_image) GObject.idle_add(self._resize_image)
@ -186,10 +172,10 @@ class CoverPanel(Gtk.Overlay):
self._cover_pixbuf = Utils.load_pixbuf(data) self._cover_pixbuf = Utils.load_pixbuf(data)
except Exception as e: except Exception as e:
self._logger.exception("Failed to set albumart") self._logger.exception("Failed to set albumart")
self._cover_pixbuf = self._get_default_image() self._cover_pixbuf = None
else: else:
# Reset image # Reset image
self._cover_pixbuf = self._get_default_image() self._cover_pixbuf = None
self._current_size = None self._current_size = None
self._current_cover_album = album self._current_cover_album = album
# Show image # Show image
@ -222,7 +208,8 @@ class CoverPanel(Gtk.Overlay):
def _enable_tracklist(self): def _enable_tracklist(self):
if self._current_album: if self._current_album:
# enable # enable
self.info_revealer.set_reveal_child(True) if not self._fullscreened:
self.info_revealer.set_reveal_child(True)
else: else:
# disable # disable
self.info_revealer.set_reveal_child(False) self.info_revealer.set_reveal_child(False)
@ -236,8 +223,11 @@ class CoverPanel(Gtk.Overlay):
def _show_image(self): def _show_image(self):
self._resize_image() if self._cover_pixbuf:
self.cover_stack.set_visible_child(self.cover_scroll) self._resize_image()
self.cover_stack.set_visible_child(self.cover_scroll)
else:
self.cover_stack.set_visible_child(self.cover_default)
self.cover_spinner.stop() self.cover_spinner.stop()
@ -246,11 +236,14 @@ class CoverPanel(Gtk.Overlay):
auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse
""" """
# Get size # Get size
size = self.cover_scroll.get_allocation() size_width = self.cover_stack.get_size(Gtk.Orientation.HORIZONTAL)
size_height = self.cover_stack.get_size(Gtk.Orientation.HORIZONTAL)
# Abort if size is the same # Abort if size is the same
if self._current_size and size.width == self._current_size.width and size.height == self._current_size.height: if self._current_size:
return current_width, current_height = self._current_size
self._current_size = size if size_width == current_width and size_height == current_height:
return
self._current_size = (size_width, size_height,)
# Get pixelbuffer # Get pixelbuffer
pixbuf = self._cover_pixbuf pixbuf = self._cover_pixbuf
@ -259,8 +252,8 @@ class CoverPanel(Gtk.Overlay):
return return
# Skalierungswert für Breite und Höhe ermitteln # Skalierungswert für Breite und Höhe ermitteln
ratioW = float(size.width) / float(pixbuf.get_width()) ratioW = float(size_width) / float(pixbuf.get_width())
ratioH = float(size.height) / float(pixbuf.get_height()) ratioH = float(size_height) / float(pixbuf.get_height())
# Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren # Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren
ratio = min(ratioW, ratioH) ratio = min(ratioW, ratioH)
ratio = min(ratio, 1) ratio = min(ratio, 1)
@ -269,15 +262,5 @@ class CoverPanel(Gtk.Overlay):
height = int(math.floor(pixbuf.get_height()*ratio)) height = int(math.floor(pixbuf.get_height()*ratio))
if width <= 0 or height <= 0: if width <= 0 or height <= 0:
return return
# Pixelpuffer auf Oberfläche zeichnen
self.cover_image.set_allocation(self.cover_scroll.get_allocation())
self.cover_image.set_from_pixbuf(pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER)) self.cover_image.set_from_pixbuf(pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER))
self.cover_image.show() self.cover_image.show()
def _get_default_image(self):
return self._icon_theme.load_icon(
Utils.STOCK_ICON_DEFAULT,
512,
Gtk.IconLookupFlags.FORCE_SVG & Gtk.IconLookupFlags.FORCE_SIZE
)

View file

@ -1,15 +0,0 @@
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
import logging
from gi.repository import Gtk, GdkPixbuf
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/info-dialog.ui')
class InfoDialog(Gtk.AboutDialog):
__gtype_name__ = 'McgInfoDialog'

View file

@ -2,120 +2,27 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
import locale import locale
import logging import logging
import math import math
import threading import threading
from gi.repository import Gtk, GObject, GdkPixbuf from gi.repository import Gtk, Gdk, GObject, GdkPixbuf, Gio, Adw
from mcg import client from mcg import client
from mcg.albumheaderbar import AlbumHeaderbar from mcg.albumheaderbar import AlbumHeaderbar
from mcg.utils import SortOrder from mcg.utils import SortOrder
from mcg.utils import Utils from mcg.utils import Utils
from mcg.utils import GridItem
from mcg.utils import SearchFilter
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/library-toolbar.ui')
class LibraryToolbar(Gtk.ButtonBox):
__gtype_name__ = 'McgLibraryToolbar'
__gsignals__ = {
'select': (GObject.SIGNAL_RUN_FIRST, None, (bool,)),
'toggle-search': (GObject.SIGNAL_RUN_FIRST, None, (bool,)),
'update': (GObject.SIGNAL_RUN_FIRST, None, ()),
'start-scale': (GObject.SIGNAL_RUN_FIRST, None, (int,)),
'end-scale': (GObject.SIGNAL_RUN_FIRST, None, (int,)),
'sort': (GObject.SIGNAL_RUN_FIRST, None, (int,)),
'sort-type': (GObject.SIGNAL_RUN_FIRST, None, (Gtk.SortType,))
}
# Widgets
select_button = Gtk.Template.Child()
toolbar_search_bar = Gtk.Template.Child()
toolbar_popover = Gtk.Template.Child()
toolbar_sort_order_button = Gtk.Template.Child()
sort_artist = Gtk.Template.Child()
sort_title = Gtk.Template.Child()
sort_year = Gtk.Template.Child()
grid_scale = Gtk.Template.Child()
def __init__(self, item_size):
super().__init__()
# Toolbar menu
self.grid_scale.set_value(item_size)
self._toolbar_sort_buttons = {
SortOrder.ARTIST: self.sort_artist,
SortOrder.TITLE: self.sort_title,
SortOrder.YEAR: self.sort_year
}
@Gtk.Template.Callback()
def on_select_toggled(self, widget):
self.emit('select', widget.get_active())
@Gtk.Template.Callback()
def on_search_toggled(self, widget):
self.emit('toggle-search', widget.get_active())
@Gtk.Template.Callback()
def on_update_clicked(self, widget):
self.emit('update')
@Gtk.Template.Callback()
def on_grid_scale_change(self, widget, scroll, value):
self.emit('start-scale', value)
@Gtk.Template.Callback()
def on_grid_scale_changed(self, widget, event):
self.emit('end-scale', self.grid_scale.get_value())
self.toolbar_popover.popdown()
@Gtk.Template.Callback()
def on_sort_toggled(self, widget):
if widget.get_active():
sort = [key for key, value in self._toolbar_sort_buttons.items() if value is widget][0]
self.emit('sort', sort)
@Gtk.Template.Callback()
def on_sort_order_toggled(self, button):
if button.get_active():
sort_type = Gtk.SortType.DESCENDING
else:
sort_type = Gtk.SortType.ASCENDING
self.emit('sort-type', sort_type)
def get_grid_scale(self):
return self.grid_scale
def is_search_active(self):
return self.toolbar_search_bar.get_active()
def set_search_active(self, active):
self.toolbar_search_bar.set_active(active)
def exit_selection(self):
self.select_button.set_active(False)
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/library-panel.ui') @Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/library-panel.ui')
class LibraryPanel(Gtk.Stack): class LibraryPanel(Adw.Bin):
__gtype_name__ = 'McgLibraryPanel' __gtype_name__ = 'McgLibraryPanel'
__gsignals__ = { __gsignals__ = {
'open-standalone': (GObject.SIGNAL_RUN_FIRST, None, ()), 'open-standalone': (GObject.SIGNAL_RUN_FIRST, None, ()),
@ -126,14 +33,27 @@ class LibraryPanel(Gtk.Stack):
'queue-multiple': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)), 'queue-multiple': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
'item-size-changed': (GObject.SIGNAL_RUN_FIRST, None, (int,)), 'item-size-changed': (GObject.SIGNAL_RUN_FIRST, None, (int,)),
'sort-order-changed': (GObject.SIGNAL_RUN_FIRST, None, (int,)), 'sort-order-changed': (GObject.SIGNAL_RUN_FIRST, None, (int,)),
'sort-type-changed': (GObject.SIGNAL_RUN_FIRST, None, (Gtk.SortType,)), 'sort-type-changed': (GObject.SIGNAL_RUN_FIRST, None, (bool,)),
'albumart': (GObject.SIGNAL_RUN_FIRST, None, (str,)) 'albumart': (GObject.SIGNAL_RUN_FIRST, None, (str,)),
} }
# Widgets # Widgets
library_stack = Gtk.Template.Child()
panel_normal = Gtk.Template.Child()
panel_standalone = Gtk.Template.Child() panel_standalone = Gtk.Template.Child()
actionbar_revealer = Gtk.Template.Child() actionbar_revealer = Gtk.Template.Child()
# Toolbar
toolbar = Gtk.Template.Child()
select_button = Gtk.Template.Child()
toolbar_search_bar = Gtk.Template.Child()
toolbar_popover = Gtk.Template.Child()
toolbar_sort_order_button = Gtk.Template.Child()
sort_artist = Gtk.Template.Child()
sort_title = Gtk.Template.Child()
sort_year = Gtk.Template.Child()
sort_modified = Gtk.Template.Child()
grid_scale = Gtk.Template.Child()
# Filter/search bar # Filter/search bar
filter_bar = Gtk.Template.Child() filter_bar = Gtk.Template.Child()
filter_entry = Gtk.Template.Child() filter_entry = Gtk.Template.Child()
@ -145,6 +65,9 @@ class LibraryPanel(Gtk.Stack):
scroll = Gtk.Template.Child() scroll = Gtk.Template.Child()
# Library Grid # Library Grid
library_grid = Gtk.Template.Child() library_grid = Gtk.Template.Child()
# Action bar (normal)
actionbar = Gtk.Template.Child()
actionbar_standalone = Gtk.Template.Child()
# Standalone Image # Standalone Image
standalone_stack = Gtk.Template.Child() standalone_stack = Gtk.Template.Child()
standalone_spinner = Gtk.Template.Child() standalone_spinner = Gtk.Template.Child()
@ -152,52 +75,51 @@ class LibraryPanel(Gtk.Stack):
standalone_image = Gtk.Template.Child() standalone_image = Gtk.Template.Child()
def __init__(self, client): def __init__(self, client, **kwargs):
super().__init__() super().__init__(**kwargs)
self._logger = logging.getLogger(__name__) self._logger = logging.getLogger(__name__)
self._client = client self._client = client
self._buttons = {} self._buttons = {}
self._albums = None self._albums = None
self._host = "localhost" self._host = "localhost"
self._filter_string = ""
self._item_size = 150 self._item_size = 150
self._sort_order = SortOrder.YEAR self._sort_order = SortOrder.YEAR
self._sort_type = Gtk.SortType.DESCENDING self._sort_type = Gtk.SortType.DESCENDING
self._grid_pixbufs = {} self._grid_pixbufs = {}
self._grid_width = 0
self._old_ranges = {} self._old_ranges = {}
self._library_lock = threading.Lock() self._library_lock = threading.Lock()
self._library_stop = threading.Event() self._library_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._standalone_pixbuf = None
self._selected_albums = [] self._selected_albums = []
self._allocation = (0, 0)
self._is_selected = False self._is_selected = False
# Widgets # Widgets
self._toolbar = LibraryToolbar(self._item_size)
self._toolbar.connect('select', self.on_toolbar_select)
self._toolbar.connect('toggle-search', self.on_toolbar_toggle_search)
self._toolbar.connect('update', self.on_toolbar_update)
self._toolbar.connect('start-scale', self.on_toolbar_scale)
self._toolbar.connect('end-scale', self.on_toolbar_scaled)
self._toolbar.connect('sort', self.on_toolbar_sort)
self._toolbar.connect('sort-type', self.on_toolbar_sort_type)
# Header bar # Header bar
self._headerbar_standalone = AlbumHeaderbar() self._headerbar_standalone = AlbumHeaderbar()
self._headerbar_standalone.connect('close', self.on_standalone_close_clicked) self._headerbar_standalone.connect('close', self.on_standalone_close_clicked)
# Progress Bar
self.progress_image.set_from_pixbuf(self._get_default_image())
# Library Grid: Model # Library Grid: Model
self._library_grid_model = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str) self._library_grid_model = Gio.ListStore()
self._library_grid_model.set_sort_func(2, self.compare_albums, self._sort_order) self._library_grid_filter = Gtk.FilterListModel()
self._library_grid_model.set_sort_column_id(2, self._sort_type) self._library_grid_filter.set_model(self._library_grid_model)
self._library_grid_filter = self._library_grid_model.filter_new() self._library_grid_selection_multi = Gtk.MultiSelection.new(self._library_grid_filter)
self._library_grid_filter.set_visible_func(self.on_filter_visible) self._library_grid_selection_single = Gtk.SingleSelection.new(self._library_grid_filter)
# Library Grid # Library Grid
self.library_grid.set_model(self._library_grid_filter) self.library_grid.set_model(self._library_grid_selection_single)
self.library_grid.set_pixbuf_column(0) # Toolbar menu
self.library_grid.set_text_column(-1) self.grid_scale.set_value(self._item_size)
self.library_grid.set_tooltip_column(1) self._toolbar_sort_buttons = {
SortOrder.ARTIST: self.sort_artist,
SortOrder.TITLE: self.sort_title,
SortOrder.YEAR: self.sort_year,
SortOrder.MODIFIED: self.sort_modified
}
# Button controller for grid scale
buttonController = Gtk.GestureClick()
buttonController.connect('unpaired-release', self.on_grid_scale_released)
self.grid_scale.add_controller(buttonController)
def get_headerbar_standalone(self): def get_headerbar_standalone(self):
@ -205,7 +127,7 @@ class LibraryPanel(Gtk.Stack):
def get_toolbar(self): def get_toolbar(self):
return self._toolbar return self.toolbar
def set_selected(self, selected): def set_selected(self, selected):
@ -213,101 +135,83 @@ class LibraryPanel(Gtk.Stack):
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_resize(self, widget, event): def on_select_toggled(self, widget):
new_allocation = (widget.get_allocation().width, widget.get_allocation().height) if self.select_button.get_active():
if new_allocation == self._allocation:
return
self._allocation = new_allocation
self._toolbar.get_grid_scale().clear_marks()
width = widget.get_allocation().width
lower = int(self._toolbar.get_grid_scale().get_adjustment().get_lower())
upper = int(self._toolbar.get_grid_scale().get_adjustment().get_upper())
countMin = max(int(width / upper), 1)
countMax = max(int(width / lower), 1)
for index in range(countMin, countMax):
pixel = int(width / index)
pixel = pixel - (2 * int(pixel / 100))
self._toolbar.get_grid_scale().add_mark(
pixel,
Gtk.PositionType.BOTTOM,
None
)
def on_toolbar_toggle_search(self, widget, active):
self.filter_bar.set_search_mode(active)
def on_toolbar_select(self, widget, active):
if active:
self.actionbar_revealer.set_reveal_child(True) self.actionbar_revealer.set_reveal_child(True)
self.library_grid.set_selection_mode(Gtk.SelectionMode.MULTIPLE) self.library_grid.set_model(self._library_grid_selection_multi)
self.library_grid.set_single_click_activate(False)
self.library_grid.get_style_context().add_class(Utils.CSS_SELECTION) self.library_grid.get_style_context().add_class(Utils.CSS_SELECTION)
else: else:
self.actionbar_revealer.set_reveal_child(False) self.actionbar_revealer.set_reveal_child(False)
self.library_grid.set_selection_mode(Gtk.SelectionMode.SINGLE) self.library_grid.set_model(self._library_grid_selection_single)
self.library_grid.set_single_click_activate(True)
self.library_grid.get_style_context().remove_class(Utils.CSS_SELECTION) self.library_grid.get_style_context().remove_class(Utils.CSS_SELECTION)
def on_toolbar_update(self, widget): @Gtk.Template.Callback()
def on_update_clicked(self, widget):
self.emit('update') self.emit('update')
def on_toolbar_scale(self, widget, value): def on_grid_scale_released(self, widget, x, y, npress, sequence):
size = math.floor(value) size = math.floor(self.grid_scale.get_value())
range = self._toolbar.get_grid_scale().get_adjustment() range = self.grid_scale.get_adjustment()
if size < range.get_lower() or size > range.get_upper(): if size < range.get_lower() or size > range.get_upper():
return return
self._item_size = size self._item_size = size
GObject.idle_add(self.library_grid.set_item_padding, size / 100)
GObject.idle_add(self._set_widget_grid_size, self.library_grid, size, True)
def on_toolbar_scaled(self, widget, value):
size = round(value)
range = self._toolbar.get_grid_scale().get_adjustment()
if size < range.get_lower() or size > range.get_upper():
return False
self.emit('item-size-changed', size) self.emit('item-size-changed', size)
self._redraw() self._redraw()
return False GObject.idle_add(self.toolbar_popover.popdown)
def on_toolbar_sort(self, widget, sort):
self._change_sort(sort)
def on_toolbar_sort_type(self, widget, sort_type):
self._sort_type = sort_type
self._library_grid_model.set_sort_column_id(2, sort_type)
self.emit('sort-type-changed', sort_type)
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_filter_bar_notify(self, widget, value): def on_grid_scale_changed(self, widget):
if self._toolbar.is_search_active() is not self.filter_bar.get_search_mode(): size = math.floor(self.grid_scale.get_value())
self._toolbar.set_search_active(self.filter_bar.get_search_mode()) range = widget.get_adjustment()
if size < range.get_lower() or size > range.get_upper():
return
self._set_widget_grid_size(self.library_grid, size, True)
@Gtk.Template.Callback()
def on_sort_toggled(self, widget):
if widget.get_active():
self._sort_order = [key for key, value in self._toolbar_sort_buttons.items() if value is widget][0]
self._sort_grid_model()
self.emit('sort-order-changed', self._sort_order)
@Gtk.Template.Callback()
def on_sort_order_toggled(self, button):
if button.get_active():
self._sort_type = Gtk.SortType.DESCENDING
else:
self._sort_type = Gtk.SortType.ASCENDING
self._sort_grid_model()
self.emit('sort-type-changed', button.get_active())
def set_size(self, width, height):
self._set_marks()
self._resize_standalone_image()
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_filter_entry_changed(self, widget): def on_filter_entry_changed(self, widget):
self._filter_string = self.filter_entry.get_text() self._library_grid_filter.set_filter(SearchFilter(self.filter_entry.get_text()))
GObject.idle_add(self._library_grid_filter.refilter)
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_library_grid_clicked(self, widget, path): def on_library_grid_clicked(self, widget, position):
# Get selected album # Get selected album
path = self._library_grid_filter.convert_path_to_child_path(path) item = self._library_grid_filter.get_item(position)
iter = self._library_grid_model.get_iter(path) album = item.get_album()
id = self._library_grid_model.get_value(iter, 2) id = album.get_id()
album = self._albums[id]
self._selected_albums = [album] self._selected_albums = [album]
self.emit('albumart', id) self.emit('albumart', id)
# Show standalone album # Show standalone album
if widget.get_selection_mode() == Gtk.SelectionMode.SINGLE: if widget.get_model() == self._library_grid_selection_single:
# Set labels # Set labels
self._headerbar_standalone.set_album(album) self._headerbar_standalone.set_album(album)
@ -319,39 +223,15 @@ class LibraryPanel(Gtk.Stack):
self.standalone_spinner.start() self.standalone_spinner.start()
@Gtk.Template.Callback()
def on_library_grid_selection_changed(self, widget):
self._selected_albums = []
for path in widget.get_selected_items():
path = self._library_grid_filter.convert_path_to_child_path(path)
iter = self._library_grid_model.get_iter(path)
id = self._library_grid_model.get_value(iter, 2)
self._selected_albums.insert(0, self._albums[id])
def on_filter_visible(self, model, iter, data):
id = model.get_value(iter, 2)
if not id in self._albums.keys():
return
album = self._albums[id]
return album.filter(self._filter_string)
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_selection_cancel_clicked(self, widget): def on_selection_cancel_clicked(self, widget):
self._toolbar.exit_selection() self.select_button.set_active(False)
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_selection_add_clicked(self, widget): def on_selection_add_clicked(self, widget):
ids = [album.get_id() for album in self._selected_albums] self.emit('queue-multiple', self._get_selected_albums())
self.emit('queue-multiple', ids) self.select_button.set_active(False)
self._toolbar.exit_selection()
@Gtk.Template.Callback()
def on_standalone_scroll_size_allocate(self, widget, allocation):
self._resize_standalone_image()
@Gtk.Template.Callback() @Gtk.Template.Callback()
@ -377,7 +257,7 @@ class LibraryPanel(Gtk.Stack):
def set_item_size(self, item_size): def set_item_size(self, item_size):
if self._item_size != item_size: if self._item_size != item_size:
self._item_size = item_size self._item_size = item_size
self._toolbar.get_grid_scale().set_value(item_size) self.grid_scale.set_value(item_size)
self._redraw() self._redraw()
@ -386,29 +266,21 @@ class LibraryPanel(Gtk.Stack):
def set_sort_order(self, sort): def set_sort_order(self, sort):
if self._sort_order != sort: button = self._toolbar_sort_buttons[sort]
button = self._toolbar_sort_buttons[sort] if button:
if button and not button.get_active(): self._sort_order = [key for key, value in self._toolbar_sort_buttons.items() if value is button][0]
if not button.get_active():
button.set_active(True) button.set_active(True)
self._sort_order = sort self._sort_grid_model()
self._library_grid_model.set_sort_func(2, self.compare_albums, self._sort_order)
def get_sort_order(self):
return self._sort_order
def set_sort_type(self, sort_type): def set_sort_type(self, sort_type):
if self._sort_type != sort_type: sort_type_gtk = Gtk.SortType.DESCENDING if sort_type else Gtk.SortType.ASCENDING
if sort_type:
sort_type_gtk = Gtk.SortType.DESCENDING if sort_type_gtk != self._sort_type:
self.toolbar_sort_order_button.set_active(True) self._sort_type = sort_type_gtk
else: self.toolbar_sort_order_button.set_active(sort_type)
sort_type_gtk = Gtk.SortType.ASCENDING self._sort_grid_model()
self.toolbar_sort_order_button.set_active(False)
if self._sort_type != sort_type_gtk:
self._sort_type = sort_type_gtk
self._library_grid_model.set_sort_column_id(2, sort_type)
def get_sort_type(self): def get_sort_type(self):
@ -444,26 +316,20 @@ class LibraryPanel(Gtk.Stack):
GObject.idle_add(self._show_image) GObject.idle_add(self._show_image)
def compare_albums(self, model, row1, row2, criterion): def _sort_grid_model(self):
id1 = model.get_value(row1, 2) GObject.idle_add(self._library_grid_model.sort, self._grid_model_compare_func, self._sort_order, self._sort_type)
id2 = model.get_value(row2, 2)
if not id1 or not id2:
return def _grid_model_compare_func(self, item1, item2, criterion, order):
return client.MCGAlbum.compare(self._albums[id1], self._albums[id2], criterion) return client.MCGAlbum.compare(item1.get_album(), item2.get_album(), criterion, (order == Gtk.SortType.DESCENDING))
def stop_threads(self): def stop_threads(self):
self._library_stop.set() self._library_stop.set()
def _change_sort(self, sort):
self._sort_order = sort
self._library_grid_model.set_sort_func(2, self.compare_albums, sort)
self.emit('sort-order-changed', sort)
def _set_albums(self, host, albums, size): def _set_albums(self, host, albums, size):
"""
if not self._is_selected and albums != self._albums: if not self._is_selected and albums != self._albums:
GObject.idle_add( GObject.idle_add(
self.get_parent().child_set_property, self.get_parent().child_set_property,
@ -471,15 +337,15 @@ class LibraryPanel(Gtk.Stack):
'needs-attention', 'needs-attention',
True True
) )
"""
self._library_lock.acquire() self._library_lock.acquire()
self._library_stop.clear()
self._albums = albums self._albums = albums
stack_transition_type = self.stack.get_transition_type()
GObject.idle_add(self.stack.set_transition_type, Gtk.StackTransitionType.NONE)
GObject.idle_add(self.stack.set_visible_child, self.progress_box) GObject.idle_add(self.stack.set_visible_child, self.progress_box)
GObject.idle_add(self.progress_bar.set_fraction, 0.0) GObject.idle_add(self.progress_bar.set_fraction, 0.0)
GObject.idle_add(self.library_grid.set_item_padding, size / 100) GObject.idle_add(self.stack.set_transition_type, stack_transition_type)
self.library_grid.set_model(None) GObject.idle_add(self._library_grid_model.remove_all)
self.library_grid.freeze_child_notify()
self._library_grid_model.clear()
i = 0 i = 0
n = len(albums) n = len(albums)
@ -494,38 +360,27 @@ class LibraryPanel(Gtk.Stack):
# Exception is handled by client # Exception is handled by client
pass pass
except Exception as e: except Exception as e:
self._logger.exception("Failed to load albumart") self._logger.exception("Failed to load albumart", e)
if pixbuf is None: if pixbuf is None:
pixbuf = self._icon_theme.load_icon( pixbuf = self._icon_theme.lookup_icon(
Utils.STOCK_ICON_DEFAULT, Utils.STOCK_ICON_DEFAULT,
None,
self._item_size, 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: if pixbuf is not None:
self._grid_pixbufs[album.get_id()] = pixbuf self._grid_pixbufs[album.get_id()] = pixbuf
self._library_grid_model.append([ GObject.idle_add(self._library_grid_model.append, GridItem(album, pixbuf))
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_id
])
i += 1 i += 1
GObject.idle_add(self.progress_bar.set_fraction, i/n) GObject.idle_add(self.progress_bar.set_fraction, i/n)
GObject.idle_add(self.progress_bar.set_text, locale.gettext("Loading images")) GObject.idle_add(self.progress_bar.set_text, locale.gettext("Loading images"))
if self._library_stop.is_set():
self._library_lock.release()
return
self.library_grid.set_model(self._library_grid_filter)
self.library_grid.thaw_child_notify()
self.library_grid.set_item_width(-1)
self._library_lock.release() self._library_lock.release()
self.stack.set_visible_child(self.scroll) GObject.idle_add(self.stack.set_visible_child, self.scroll)
self._sort_grid_model()
def _set_widget_grid_size(self, grid_widget, size, vertical): def _set_widget_grid_size(self, grid_widget, size, vertical):
@ -536,50 +391,31 @@ class LibraryPanel(Gtk.Stack):
def _set_widget_grid_size_thread(self, grid_widget, size, vertical): def _set_widget_grid_size_thread(self, grid_widget, size, vertical):
self._library_lock.acquire() self._library_lock.acquire()
self._library_stop.clear() self._library_stop.clear()
grid_filter = grid_widget.get_model() if size == self._item_size:
grid_model = grid_filter.get_model()
# get old_range
grid_widget_id = id(grid_widget)
if grid_widget_id not in self._old_ranges or self._old_ranges[grid_widget_id] is None:
self._old_ranges[grid_widget_id] = range(0, len(grid_filter))
old_range = self._old_ranges[grid_widget_id]
old_start = len(old_range) > 0 and old_range[0] or 0
old_end = len(old_range) > 0 and old_range[len(old_range)-1] + 1 or 0
# calculate visible range
w = (grid_widget.get_allocation().width // size) + (vertical and 0 or 1)
h = (grid_widget.get_allocation().height // size) + (vertical and 1 or 0)
c = w * h
vis_range = grid_widget.get_visible_range()
if vis_range is None:
self._library_lock.release() self._library_lock.release()
return return
(vis_start,), (vis_end,) = vis_range for i in range(self._library_grid_model.get_n_items()):
vis_end = min(vis_start + c, len(grid_filter)) grid_item = self._library_grid_model.get_item(i)
vis_range = range(vis_start, vis_end) album_id = grid_item.get_album().get_id()
# set pixbuf pixbuf = self._grid_pixbufs[album_id]
cur_start = min(old_start, vis_start) if pixbuf is not None:
cur_end = max(old_end, vis_end)
cur_range = range(cur_start, cur_end)
for index in cur_range:
iter = grid_filter.convert_iter_to_child_iter(grid_filter[index].iter)
if index in vis_range:
album_id = grid_model.get_value(iter, 2)
pixbuf = self._grid_pixbufs[album_id]
pixbuf = pixbuf.scale_simple(size, size, GdkPixbuf.InterpType.NEAREST) pixbuf = pixbuf.scale_simple(size, size, GdkPixbuf.InterpType.NEAREST)
else: else:
pixbuf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, 1, 1) pixbuf = self._icon_theme.lookup_icon(
grid_model.set_value(iter, 0, pixbuf) Utils.STOCK_ICON_DEFAULT,
None,
size,
size,
Gtk.TextDirection.LTR,
Gtk.IconLookupFlags.FORCE_SYMBOLIC
)
GObject.idle_add(grid_item.set_cover, pixbuf)
if self._library_stop.is_set(): if self._library_stop.is_set():
self._library_lock.release() self._library_lock.release()
return return
self._old_ranges[grid_widget_id] = vis_range
grid_widget.set_item_width(size)
self._library_lock.release() self._library_lock.release()
@ -594,13 +430,34 @@ class LibraryPanel(Gtk.Stack):
self.set_albums(self._host, self._albums) self.set_albums(self._host, self._albums)
def _set_marks(self):
width = self.scroll.get_width()
if width == self._grid_width:
return
self._grid_width = width
self.grid_scale.clear_marks()
lower = int(self.grid_scale.get_adjustment().get_lower())
upper = int(self.grid_scale.get_adjustment().get_upper())
countMin = max(int(width / upper), 1)
countMax = max(int(width / lower), 1)
for index in range(countMin, countMax):
pixel = int(width / index)
pixel = pixel - (2 * int(pixel / 100))
self.grid_scale.add_mark(
pixel,
Gtk.PositionType.BOTTOM,
None
)
def _open_standalone(self): def _open_standalone(self):
self.set_visible_child(self.panel_standalone) self.library_stack.set_visible_child(self.panel_standalone)
self.emit('open-standalone') self.emit('open-standalone')
def _close_standalone(self): def _close_standalone(self):
self.set_visible_child(self.get_children()[0]) self.library_stack.set_visible_child(self.panel_normal)
self.emit('close-standalone') self.emit('close-standalone')
@ -608,15 +465,19 @@ class LibraryPanel(Gtk.Stack):
"""Diese Methode skaliert das geladene Bild aus dem Pixelpuffer """Diese Methode skaliert das geladene Bild aus dem Pixelpuffer
auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse 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 pixbuf = self._standalone_pixbuf
size = self.standalone_scroll.get_allocation()
# Check pixelbuffer # Check pixelbuffer
if pixbuf is None: if pixbuf is None:
return return
# Skalierungswert für Breite und Höhe ermitteln # Skalierungswert für Breite und Höhe ermitteln
ratioW = float(size.width) / float(pixbuf.get_width()) ratioW = float(size_width) / float(pixbuf.get_width())
ratioH = float(size.height) / float(pixbuf.get_height()) ratioH = float(size_height) / float(pixbuf.get_height())
# Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren # Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren
ratio = min(ratioW, ratioH) ratio = min(ratioW, ratioH)
ratio = min(ratio, 1) ratio = min(ratio, 1)
@ -626,18 +487,24 @@ class LibraryPanel(Gtk.Stack):
if width <= 0 or height <= 0: if width <= 0 or height <= 0:
return return
# Pixelpuffer auf Oberfläche zeichnen # 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.set_from_pixbuf(pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER))
self.standalone_image.show() self.standalone_image.show()
def _get_default_image(self): def _get_default_image(self):
return self._icon_theme.load_icon( return self._icon_theme.lookup_icon(
Utils.STOCK_ICON_DEFAULT, Utils.STOCK_ICON_DEFAULT,
None,
512, 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.library_grid.get_model().get_n_items()):
if self.library_grid.get_model().is_selected(i):
albums.append(self.library_grid.get_model().get_item(i).get_album())
return albums

View file

@ -1,6 +1,4 @@
import sys import sys
import gi
gi.require_version('Gtk', '3.0')
from .application import Application from .application import Application

View file

@ -25,7 +25,6 @@ mcg_sources = [
'client.py', 'client.py',
'connectionpanel.py', 'connectionpanel.py',
'coverpanel.py', 'coverpanel.py',
'infodialog.py',
'librarypanel.py', 'librarypanel.py',
'playlistpanel.py', 'playlistpanel.py',
'serverpanel.py', 'serverpanel.py',

View file

@ -2,56 +2,24 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
import logging import logging
import math import math
import threading 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 import client
from mcg.albumheaderbar import AlbumHeaderbar from mcg.albumheaderbar import AlbumHeaderbar
from mcg.utils import Utils from mcg.utils import Utils
from mcg.utils import GridItem
@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)
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/playlist-panel.ui') @Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/playlist-panel.ui')
class PlaylistPanel(Gtk.Stack): class PlaylistPanel(Adw.Bin):
__gtype_name__ = 'McgPlaylistPanel' __gtype_name__ = 'McgPlaylistPanel'
__gsignals__ = { __gsignals__ = {
'open-standalone': (GObject.SIGNAL_RUN_FIRST, None, ()), '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-album': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
'remove-multiple-albums': (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,)), 'play': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
'albumart': (GObject.SIGNAL_RUN_FIRST, None, (str,)) 'albumart': (GObject.SIGNAL_RUN_FIRST, None, (str,)),
} }
# Widgets # Widgets
playlist_stack = Gtk.Template.Child()
panel_normal = Gtk.Template.Child()
panel_standalone = Gtk.Template.Child() panel_standalone = Gtk.Template.Child()
actionbar_revealer = 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
playlist_grid = Gtk.Template.Child() playlist_grid = Gtk.Template.Child()
# Action bar (normal) # Action bar (normal)
@ -79,8 +53,8 @@ class PlaylistPanel(Gtk.Stack):
standalone_image = Gtk.Template.Child() standalone_image = Gtk.Template.Child()
def __init__(self, client): def __init__(self, client, **kwargs):
GObject.GObject.__init__(self) super().__init__(**kwargs)
self._client = client self._client = client
self._host = None self._host = None
self._item_size = 150 self._item_size = 150
@ -88,25 +62,21 @@ class PlaylistPanel(Gtk.Stack):
self._playlist_albums = None self._playlist_albums = None
self._playlist_lock = threading.Lock() self._playlist_lock = threading.Lock()
self._playlist_stop = threading.Event() 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._standalone_pixbuf = None
self._selected_albums = [] self._selected_albums = []
self._is_selected = False self._is_selected = False
# Widgets # Widgets
self._toolbar = PlaylistToolbar()
self._toolbar.connect('select', self.on_toolbar_select)
self._toolbar.connect('clear-playlist', self.on_toolbar_clear)
# Header bar # Header bar
self._headerbar_standalone = AlbumHeaderbar() self._headerbar_standalone = AlbumHeaderbar()
self._headerbar_standalone.connect('close', self.on_headerbar_close_clicked) self._headerbar_standalone.connect('close', self.on_headerbar_close_clicked)
# Playlist Grid: Model # 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 # Playlist Grid
self.playlist_grid.set_model(self._playlist_grid_model) self.playlist_grid.set_model(self._playlist_grid_selection_single)
self.playlist_grid.set_pixbuf_column(0)
self.playlist_grid.set_text_column(-1)
self.playlist_grid.set_tooltip_column(1)
def get_headerbar_standalone(self): def get_headerbar_standalone(self):
@ -114,39 +84,43 @@ class PlaylistPanel(Gtk.Stack):
def get_toolbar(self): def get_toolbar(self):
return self._toolbar return self.toolbar
def set_selected(self, selected): def set_selected(self, selected):
self._is_selected = selected self._is_selected = selected
def on_toolbar_select(self, widget, active): @Gtk.Template.Callback()
if active: def on_select_toggled(self, widget):
if self.select_button.get_active():
self.actionbar_revealer.set_reveal_child(True) 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) self.playlist_grid.get_style_context().add_class(Utils.CSS_SELECTION)
else: else:
self.actionbar_revealer.set_reveal_child(False) 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) 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') self.emit('clear-playlist')
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_playlist_grid_clicked(self, widget, path): def on_playlist_grid_clicked(self, widget, position):
# Get selected album # Get selected album
iter = self._playlist_grid_model.get_iter(path) item = self._playlist_grid_model.get_item(position)
hash = self._playlist_grid_model.get_value(iter, 2) album = item.get_album()
album = self._playlist_albums[hash] id = album.get_id()
self._selected_albums = [album] self._selected_albums = [album]
self.emit('albumart', hash) self.emit('albumart', id)
# Show standalone album # Show standalone album
if widget.get_selection_mode() == Gtk.SelectionMode.SINGLE: if widget.get_model() == self._playlist_grid_selection_single:
# Set labels # Set labels
self._headerbar_standalone.set_album(album) self._headerbar_standalone.set_album(album)
@ -158,29 +132,15 @@ class PlaylistPanel(Gtk.Stack):
self.standalone_spinner.start() 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() @Gtk.Template.Callback()
def on_selection_cancel_clicked(self, widget): def on_selection_cancel_clicked(self, widget):
self._toolbar.exit_selection() self.select_button.set_active(False)
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_selection_remove_clicked(self, widget): def on_selection_remove_clicked(self, widget):
self.emit('remove-multiple-albums', self._selected_albums) self.emit('remove-multiple-albums', self._get_selected_albums())
self._toolbar.exit_selection() self.select_button.set_active(False)
@Gtk.Template.Callback()
def on_standalone_scroll_size_allocate(self, widget, allocation):
self._resize_standalone_image()
def on_headerbar_close_clicked(self, widget): def on_headerbar_close_clicked(self, widget):
@ -189,16 +149,20 @@ class PlaylistPanel(Gtk.Stack):
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_standalone_remove_clicked(self, widget): 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() self._close_standalone()
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_standalone_play_clicked(self, widget): 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() self._close_standalone()
def set_size(self, width, height):
self._resize_standalone_image()
def set_item_size(self, item_size): def set_item_size(self, item_size):
if self._item_size != item_size: if self._item_size != item_size:
self._item_size = item_size self._item_size = item_size
@ -235,6 +199,7 @@ class PlaylistPanel(Gtk.Stack):
def _set_playlist(self, host, playlist, size): def _set_playlist(self, host, playlist, size):
"""
if not self._is_selected and self._playlist != playlist: if not self._is_selected and self._playlist != playlist:
GObject.idle_add( GObject.idle_add(
self.get_parent().child_set_property, self.get_parent().child_set_property,
@ -242,16 +207,14 @@ class PlaylistPanel(Gtk.Stack):
'needs-attention', 'needs-attention',
True True
) )
"""
self._playlist_lock.acquire() self._playlist_lock.acquire()
self._playlist_stop.clear() self._playlist_stop.clear()
self._playlist = playlist self._playlist = playlist
self._playlist_albums = {} self._playlist_albums = {}
for album in playlist: for album in playlist:
self._playlist_albums[album.get_id()] = album self._playlist_albums[album.get_id()] = album
self.playlist_grid.set_model(None) self._playlist_grid_model.remove_all()
self.playlist_grid.freeze_child_notify()
self._playlist_grid_model.clear()
GObject.idle_add(self.playlist_grid.set_item_padding, size / 100)
cache = client.MCGCache(host, size) cache = client.MCGCache(host, size)
for album in playlist: for album in playlist:
@ -265,31 +228,22 @@ class PlaylistPanel(Gtk.Stack):
except Exception: except Exception:
self._logger.exception("Failed to load albumart") self._logger.exception("Failed to load albumart")
if pixbuf is None: if pixbuf is None:
pixbuf = self._icon_theme.load_icon( pixbuf = self._icon_theme.lookup_icon(
Utils.STOCK_ICON_DEFAULT, Utils.STOCK_ICON_DEFAULT,
None,
self._item_size, 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: if pixbuf is not None:
self._playlist_grid_model.append([ self._playlist_grid_model.append(GridItem(album, pixbuf))
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()
])
if self._playlist_stop.is_set(): if self._playlist_stop.is_set():
self._playlist_lock.release() self._playlist_lock.release()
return return
self.playlist_grid.set_model(self._playlist_grid_model) self.playlist_grid.set_model(self._playlist_grid_selection_single)
self.playlist_grid.thaw_child_notify()
# TODO why set_columns()?
#self.playlist_grid.set_columns(len(playlist))
self._playlist_lock.release() self._playlist_lock.release()
@ -305,12 +259,12 @@ class PlaylistPanel(Gtk.Stack):
def _open_standalone(self): def _open_standalone(self):
self.set_visible_child(self.panel_standalone) self.playlist_stack.set_visible_child(self.panel_standalone)
self.emit('open-standalone') self.emit('open-standalone')
def _close_standalone(self): 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') self.emit('close-standalone')
@ -318,15 +272,19 @@ class PlaylistPanel(Gtk.Stack):
"""Diese Methode skaliert das geladene Bild aus dem Pixelpuffer """Diese Methode skaliert das geladene Bild aus dem Pixelpuffer
auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse 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 pixbuf = self._standalone_pixbuf
size = self.standalone_scroll.get_allocation()
# Check pixelbuffer # Check pixelbuffer
if pixbuf is None: if pixbuf is None:
return return
# Skalierungswert für Breite und Höhe ermitteln # Skalierungswert für Breite und Höhe ermitteln
ratioW = float(size.width) / float(pixbuf.get_width()) ratioW = float(size_width) / float(pixbuf.get_width())
ratioH = float(size.height) / float(pixbuf.get_height()) ratioH = float(size_height) / float(pixbuf.get_height())
# Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren # Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren
ratio = min(ratioW, ratioH) ratio = min(ratioW, ratioH)
ratio = min(ratio, 1) ratio = min(ratio, 1)
@ -336,18 +294,24 @@ class PlaylistPanel(Gtk.Stack):
if width <= 0 or height <= 0: if width <= 0 or height <= 0:
return return
# Pixelpuffer auf Oberfläche zeichnen # 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.set_from_pixbuf(pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER))
self.standalone_image.show() self.standalone_image.show()
def _get_default_image(self): def _get_default_image(self):
return self._icon_theme.load_icon( return self._icon_theme.lookup_icon(
Utils.STOCK_ICON_DEFAULT, Utils.STOCK_ICON_DEFAULT,
None,
512, 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

View file

@ -2,32 +2,23 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, GObject from gi.repository import Gtk, Adw, GObject
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/server-toolbar.ui')
class ServerToolbar(Gtk.ButtonBox):
__gtype_name__ = 'McgServerToolbar'
def __init__(self):
super().__init__()
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/server-panel.ui') @Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/server-panel.ui')
class ServerPanel(Gtk.Box): class ServerPanel(Adw.Bin):
__gtype_name__ = 'McgServerPanel' __gtype_name__ = 'McgServerPanel'
__gsignals__ = { __gsignals__ = {
'change-output-device': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,bool,)), 'change-output-device': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,bool,)),
} }
# Widgets # Widgets
toolbar = Gtk.Template.Child()
# Status widgets # Status widgets
status_file = Gtk.Template.Child() status_file = Gtk.Template.Child()
status_audio = Gtk.Template.Child() status_audio = Gtk.Template.Child()
@ -44,14 +35,13 @@ class ServerPanel(Gtk.Box):
output_devices = Gtk.Template.Child() output_devices = Gtk.Template.Child()
def __init__(self): def __init__(self, **kwargs):
super().__init__() super().__init__(**kwargs)
self._none_label = "" self._none_label = ""
self._output_buttons = {} self._output_buttons = {}
self._is_selected = False self._is_selected = False
# Widgets # Widgets
self._toolbar = ServerToolbar()
self._none_label = self.status_file.get_label() self._none_label = self.status_file.get_label()
@ -60,7 +50,7 @@ class ServerPanel(Gtk.Box):
def get_toolbar(self): def get_toolbar(self):
return self._toolbar return self.toolbar
def on_output_device_toggled(self, widget, device): def on_output_device_toggled(self, widget, device):
@ -115,13 +105,12 @@ class ServerPanel(Gtk.Box):
self._output_buttons[device.get_id()].set_active(device.is_enabled()) self._output_buttons[device.get_id()].set_active(device.is_enabled())
self._output_buttons[device.get_id()].thaw_notify() self._output_buttons[device.get_id()].thaw_notify()
else: else:
button = Gtk.CheckButton(device.get_name()) button = Gtk.CheckButton.new_with_label(device.get_name())
if device.is_enabled(): if device.is_enabled():
button.set_active(True) button.set_active(True)
handler = button.connect('toggled', self.on_output_device_toggled, device) handler = button.connect('toggled', self.on_output_device_toggled, device)
self.output_devices.insert(button, -1) self.output_devices.insert(button, -1)
self._output_buttons[device.get_id()] = button self._output_buttons[device.get_id()] = button
self.output_devices.show_all()
# Remove devices # Remove devices
for id in self._output_buttons.keys(): for id in self._output_buttons.keys():

View file

@ -2,8 +2,8 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk from gi.repository import Gtk

View file

@ -2,13 +2,13 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '4.0')
import hashlib import hashlib
import locale import locale
import os import os
import urllib import urllib
from gi.repository import GdkPixbuf from gi.repository import Gdk, GdkPixbuf, GObject, Gtk
@ -86,3 +86,48 @@ class SortOrder:
ARTIST = 0 ARTIST = 0
TITLE = 1 TITLE = 1
YEAR = 2 YEAR = 2
MODIFIED = 3
class GridItem(GObject.GObject):
__gtype_name__ = "GridItem"
tooltip = GObject.Property(type=str, default=None)
cover = GObject.Property(type=Gdk.Paintable, default=None)
def __init__(self, album, cover):
super().__init__()
self._album = album
if cover:
self.cover = Gdk.Texture.new_for_pixbuf(cover)
self.tooltip = GObject.markup_escape_text("\n".join([
album.get_title(),
', '.join(album.get_dates()),
Utils.create_artists_label(album),
Utils.create_length_label(album)
]))
def get_album(self):
return self._album
def set_cover(self, cover):
self.cover = Gdk.Texture.new_for_pixbuf(cover)
class SearchFilter(Gtk.Filter):
def __init__(self, search_string):
super().__init__()
self._search_string = search_string
def do_match(self, grid_item):
return grid_item.get_album().filter(self._search_string)

View file

@ -2,16 +2,18 @@
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
try: try:
import keyring import keyring
use_keyring = True use_keyring = True
except: except:
use_keyring = False use_keyring = False
use_keyring = False
import locale import locale
import logging import logging
from gi.repository import Gtk, Gdk, GObject, GLib, Gio from gi.repository import Gtk, Adw, Gdk, GObject, GLib, Gio
from . import client from . import client
from .shortcutsdialog import ShortcutsDialog from .shortcutsdialog import ShortcutsDialog
@ -43,7 +45,7 @@ class WindowState(GObject.Object):
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/window.ui') @Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/window.ui')
class Window(Gtk.ApplicationWindow): class Window(Adw.ApplicationWindow):
__gtype_name__ = 'McgAppWindow' __gtype_name__ = 'McgAppWindow'
SETTING_HOST = 'host' SETTING_HOST = 'host'
SETTING_PORT = 'port' SETTING_PORT = 'port'
@ -58,33 +60,33 @@ class Window(Gtk.ApplicationWindow):
_CUSTOM_STARTUP_COMPLETE = 'startup-complete' _CUSTOM_STARTUP_COMPLETE = 'startup-complete'
# Widgets # Widgets
toolbar_view = Gtk.Template.Child()
content_stack = Gtk.Template.Child() content_stack = Gtk.Template.Child()
panel_stack = Gtk.Template.Child() panel_stack = Gtk.Template.Child()
toolbar_stack = Gtk.Template.Child() toolbar_stack = Gtk.Template.Child()
# Headerbar # Headerbar
headerbar = Gtk.Template.Child() headerbar = Gtk.Template.Child()
headerbar_title_stack = Gtk.Template.Child() #headerbar_title_stack = Gtk.Template.Child()
headerbar_panel_switcher = Gtk.Template.Child() headerbar_panel_switcher = Gtk.Template.Child()
headerbar_connection_label = Gtk.Template.Child() #headerbar_connection_label = Gtk.Template.Child()
headerbar_button_connect = Gtk.Template.Child() headerbar_button_connect = Gtk.Template.Child()
headerbar_button_playpause = Gtk.Template.Child() headerbar_button_playpause = Gtk.Template.Child()
headerbar_button_volume = Gtk.Template.Child() headerbar_button_volume = Gtk.Template.Child()
# Infobar # Infobar
info_revealer = Gtk.Template.Child() info_toast = Gtk.Template.Child()
info_bar = Gtk.Template.Child()
info_label = Gtk.Template.Child()
def __init__(self, app, title, settings): def __init__(self, app, title, settings, **kwargs):
super().__init__() super().__init__(**kwargs)
self.set_application(app) self.set_application(app)
self.set_title(title) self.set_title(title)
self._settings = settings self._settings = settings
self._panels = [] self._panels = []
self._mcg = client.Client() self._mcg = client.Client()
self._state = WindowState() self._state = WindowState()
self._changing_volume = False
self._setting_volume = False self._setting_volume = False
self._headerbar_connection_button_active = True
self._headerbar_playpause_button_active = True
# Help/Shortcuts dialog # Help/Shortcuts dialog
self.set_help_overlay(ShortcutsDialog()) self.set_help_overlay(ShortcutsDialog())
@ -108,19 +110,19 @@ class Window(Gtk.ApplicationWindow):
self._library_panel.connect('close-standalone', self.on_panel_close_standalone) self._library_panel.connect('close-standalone', self.on_panel_close_standalone)
self._panels.append(self._library_panel) self._panels.append(self._library_panel)
# Stack # Stack
self.content_stack.add(self._connection_panel) self.content_stack.add_child(self._connection_panel)
self.panel_stack.add_titled(self._server_panel, 'server-panel', locale.gettext("Server")) self.panel_stack.add_titled_with_icon(self._server_panel, 'server-panel', locale.gettext("Server"), "network-wired-symbolic")
self.panel_stack.add_titled(self._cover_panel, 'cover-panel', locale.gettext("Cover")) self.panel_stack.add_titled_with_icon(self._cover_panel, 'cover-panel', locale.gettext("Cover"), "image-x-generic-symbolic")
self.panel_stack.add_titled(self._playlist_panel, 'playlist-panel', locale.gettext("Playlist")) self.panel_stack.add_titled_with_icon(self._playlist_panel, 'playlist-panel', locale.gettext("Playlist"), "view-list-symbolic")
self.panel_stack.add_titled(self._library_panel, 'library-panel', locale.gettext("Library")) self.panel_stack.add_titled_with_icon(self._library_panel, 'library-panel', locale.gettext("Library"), "emblem-music-symbolic")
# Header # Header
self._playlist_panel.get_headerbar_standalone().connect('close', self.on_panel_close_standalone) #self._playlist_panel.get_headerbar_standalone().connect('close', self.on_panel_close_standalone)
self._library_panel.get_headerbar_standalone().connect('close', self.on_panel_close_standalone) #self._library_panel.get_headerbar_standalone().connect('close', self.on_panel_close_standalone)
# Toolbar stack # Toolbar stack
self.toolbar_stack.add(self._server_panel.get_toolbar()) self.toolbar_stack.add_child(self._server_panel.get_toolbar())
self.toolbar_stack.add(self._cover_panel.get_toolbar()) self.toolbar_stack.add_child(self._cover_panel.get_toolbar())
self.toolbar_stack.add(self._playlist_panel.get_toolbar()) self.toolbar_stack.add_child(self._playlist_panel.get_toolbar())
self.toolbar_stack.add(self._library_panel.get_toolbar()) self.toolbar_stack.add_child(self._library_panel.get_toolbar())
# Properties # Properties
self._set_headerbar_sensitive(False, False) self._set_headerbar_sensitive(False, False)
@ -134,6 +136,10 @@ class Window(Gtk.ApplicationWindow):
self._library_panel.set_sort_type(self._settings.get_boolean(Window.SETTING_SORT_TYPE)) self._library_panel.set_sort_type(self._settings.get_boolean(Window.SETTING_SORT_TYPE))
# Signals # Signals
self.connect("notify::default-width", self.on_resize)
self.connect("notify::default-height", self.on_resize)
self.connect("notify::maximized", self.on_maximized)
self.connect("notify::fullscreened", self.on_fullscreened)
self._connection_panel.connect('connection-changed', self.on_connection_panel_connection_changed) self._connection_panel.connect('connection-changed', self.on_connection_panel_connection_changed)
self.panel_stack.connect('notify::visible-child', self.on_stack_switched) self.panel_stack.connect('notify::visible-child', self.on_stack_switched)
self._server_panel.connect('change-output-device', self.on_server_panel_output_device_changed) self._server_panel.connect('change-output-device', self.on_server_panel_output_device_changed)
@ -176,7 +182,6 @@ class Window(Gtk.ApplicationWindow):
self.set_default_size(self._state.width, self._state.height) self.set_default_size(self._state.width, self._state.height)
if self._state.get_property(WindowState.IS_MAXIMIZED): if self._state.get_property(WindowState.IS_MAXIMIZED):
self.maximize() self.maximize()
self.show_all()
self.content_stack.set_visible_child(self._connection_panel) self.content_stack.set_visible_child(self._connection_panel)
if self._settings.get_boolean(Window.SETTING_CONNECTED): if self._settings.get_boolean(Window.SETTING_CONNECTED):
self._connect() self._connect()
@ -236,52 +241,38 @@ class Window(Gtk.ApplicationWindow):
def on_menu_search_library(self, action, value): def on_menu_search_library(self, action, value):
self.panel_stack.set_visible_child(self._library_panel) self.panel_stack.set_visible_child(self.library_panel_page)
self._library_panel.show_search() self._library_panel.show_search()
# Window callbacks # Window callbacks
@Gtk.Template.Callback()
def on_resize(self, widget, event): def on_resize(self, widget, event):
if not self._state.get_property(WindowState.IS_MAXIMIZED): width = self.get_size(Gtk.Orientation.HORIZONTAL)
size = self.get_size() height = self.get_size(Gtk.Orientation.VERTICAL)
self._state.set_property(WindowState.WIDTH, size.width) if width > 0:
self._state.set_property(WindowState.HEIGHT, size.height) self._cover_panel.set_width(width)
if not self._state.get_property(WindowState.IS_MAXIMIZED):
self._state.set_property(WindowState.WIDTH, width)
self._state.set_property(WindowState.HEIGHT, height)
GObject.idle_add(self._playlist_panel.set_size, width, height)
GObject.idle_add(self._library_panel.set_size, width, height)
@Gtk.Template.Callback() def on_maximized(self, widget, maximized):
def on_state(self, widget, state): self._state.set_property(WindowState.IS_MAXIMIZED, maximized is True)
self._state.set_property(WindowState.IS_MAXIMIZED, (state.new_window_state & Gdk.WindowState.MAXIMIZED > 0))
self._fullscreen((state.new_window_state & Gdk.WindowState.FULLSCREEN > 0))
def on_fullscreened(self, widget, fullscreened):
self._fullscreen(self.is_fullscreen())
# HeaderBar callbacks # HeaderBar callbacks
@Gtk.Template.Callback()
def on_headerbar_connection_active_notify(self, widget, status):
self._connect()
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_headerbar_connection_state_set(self, widget, state): def on_headerbar_connection_state_set(self, widget, state):
return True if self._headerbar_connection_button_active:
self._connect()
@Gtk.Template.Callback()
def on_headerbar_volume_press(self, widget, active):
self._changing_volume = active
@Gtk.Template.Callback()
def on_headerbar_volume_release(self, widget, active):
self._changing_volume = active
@Gtk.Template.Callback()
def on_headerbar_playpause_toggled(self, widget):
self._mcg.playpause()
self._mcg.get_status()
@Gtk.Template.Callback() @Gtk.Template.Callback()
@ -290,17 +281,11 @@ class Window(Gtk.ApplicationWindow):
self._mcg.set_volume(int(value*100)) self._mcg.set_volume(int(value*100))
# Infobar callback
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_info_bar_close(self, *args): def on_headerbar_playpause_toggled(self, widget):
self.info_revealer.set_reveal_child(False) if self._headerbar_playpause_button_active:
self._mcg.playpause()
self._mcg.get_status()
@Gtk.Template.Callback()
def on_info_bar_response(self, widget, response):
self.info_revealer.set_reveal_child(False)
# Panel callbacks # Panel callbacks
@ -311,19 +296,23 @@ class Window(Gtk.ApplicationWindow):
self._set_menu_visible_panel() self._set_menu_visible_panel()
for panel in self._panels: for panel in self._panels:
panel.set_selected(panel == self.panel_stack.get_visible_child()) panel.set_selected(panel == self.panel_stack.get_visible_child())
GObject.idle_add( #GObject.idle_add(
self.panel_stack.child_set_property, # self.panel_stack.child_set_property,
self.panel_stack.get_visible_child(), # self.panel_stack.get_visible_child(),
'needs-attention', # 'needs-attention',
False # False
) #)
def on_panel_open_standalone(self, panel): def on_panel_open_standalone(self, panel):
self.set_titlebar(panel.get_headerbar_standalone()) self.toolbar_view.add_top_bar(panel.get_headerbar_standalone())
self.toolbar_view.remove(self.headerbar)
def on_panel_close_standalone(self, headerbar): def on_panel_close_standalone(self, panel):
self.set_titlebar(self.headerbar) self.toolbar_view.add_top_bar(self.headerbar)
self.toolbar_view.remove(panel.get_headerbar_standalone())
def on_connection_panel_connection_changed(self, widget, host, port, password): def on_connection_panel_connection_changed(self, widget, host, port, password):
self._settings.set_string(Window.SETTING_HOST, host) self._settings.set_string(Window.SETTING_HOST, host)
@ -397,11 +386,11 @@ class Window(Gtk.ApplicationWindow):
def on_library_panel_sort_order_changed(self, widget, sort_order): def on_library_panel_sort_order_changed(self, widget, sort_order):
self._settings.set_enum(Window.SETTING_SORT_ORDER, self._library_panel.get_sort_order()) self._settings.set_enum(Window.SETTING_SORT_ORDER, sort_order)
def on_library_panel_sort_type_changed(self, widget, sort_type): def on_library_panel_sort_type_changed(self, widget, sort_type):
self._settings.set_boolean(Window.SETTING_SORT_TYPE, self._library_panel.get_sort_type()) self._settings.set_boolean(Window.SETTING_SORT_TYPE, sort_type)
def on_library_panel_albumart(self, widget, album): def on_library_panel_albumart(self, widget, album):
@ -450,9 +439,7 @@ class Window(Gtk.ApplicationWindow):
# Status # Status
self._server_panel.set_status(file, audio, bitrate, error) self._server_panel.set_status(file, audio, bitrate, error)
# Error # Error
if error is None: if error:
self.info_revealer.set_reveal_child(False)
else:
self._show_error(error) self._show_error(error)
@ -487,6 +474,8 @@ class Window(Gtk.ApplicationWindow):
def on_mcg_custom(self, name): def on_mcg_custom(self, name):
pass
"""
if name == Window._CUSTOM_STARTUP_COMPLETE: if name == Window._CUSTOM_STARTUP_COMPLETE:
for panel in self._panels: for panel in self._panels:
GObject.idle_add( GObject.idle_add(
@ -495,6 +484,7 @@ class Window(Gtk.ApplicationWindow):
'needs-attention', 'needs-attention',
False False
) )
"""
def on_mcg_error(self, error): def on_mcg_error(self, error):
@ -563,17 +553,11 @@ class Window(Gtk.ApplicationWindow):
if self._state.get_property(WindowState.IS_FULLSCREENED): if self._state.get_property(WindowState.IS_FULLSCREENED):
self.headerbar.hide() self.headerbar.hide()
self._cover_panel.set_fullscreen(True) self._cover_panel.set_fullscreen(True)
# Hide cursor self.set_cursor(Gdk.Cursor.new_from_name("none", None))
self.get_window().set_cursor(
Gdk.Cursor.new_from_name(Gdk.Display.get_default(), "none")
)
else: else:
self.headerbar.show() self.headerbar.show()
self._cover_panel.set_fullscreen(False) self._cover_panel.set_fullscreen(False)
# Reset cursor self.set_cursor(Gdk.Cursor.new_from_name("default", None))
self.get_window().set_cursor(
Gdk.Cursor.new_from_name(Gdk.Display.get_default(), "default")
)
def _save_visible_panel(self): def _save_visible_panel(self):
@ -593,58 +577,39 @@ class Window(Gtk.ApplicationWindow):
def _set_play(self): def _set_play(self):
self.headerbar_button_playpause.handler_block_by_func( self._headerbar_playpause_button_active = False
self.on_headerbar_playpause_toggled
)
self.headerbar_button_playpause.set_active(True) self.headerbar_button_playpause.set_active(True)
self.headerbar_button_playpause.handler_unblock_by_func( self._headerbar_playpause_button_active = True
self.on_headerbar_playpause_toggled
)
def _set_pause(self): def _set_pause(self):
self.headerbar_button_playpause.handler_block_by_func( self._headerbar_playpause_button_active = False
self.on_headerbar_playpause_toggled
)
self.headerbar_button_playpause.set_active(False) self.headerbar_button_playpause.set_active(False)
self.headerbar_button_playpause.handler_unblock_by_func( self._headerbar_playpause_button_active = True
self.on_headerbar_playpause_toggled
)
def _set_volume(self, volume): def _set_volume(self, volume):
if volume >= 0: if volume >= 0:
self.headerbar_button_volume.set_visible(True) self.headerbar_button_volume.set_visible(True)
if not self._changing_volume: self._setting_volume = True
self._setting_volume = True self.headerbar_button_volume.set_value(volume / 100)
self.headerbar_button_volume.set_value(volume / 100) self._setting_volume = False
self._setting_volume = False
else: else:
self.headerbar_button_volume.set_visible(False) self.headerbar_button_volume.set_visible(False)
def _headerbar_connected(self): def _headerbar_connected(self):
self.headerbar_button_connect.handler_block_by_func( self._headerbar_connection_button_active = False
self.on_headerbar_connection_active_notify
)
self.headerbar_button_connect.set_active(True) self.headerbar_button_connect.set_active(True)
self.headerbar_button_connect.set_state(True) self.headerbar_button_connect.set_state(True)
self.headerbar_button_connect.handler_unblock_by_func( self._headerbar_connection_button_active = True
self.on_headerbar_connection_active_notify
)
self.headerbar_title_stack.set_visible_child(self.headerbar_panel_switcher)
def _headerbar_disconnected(self): def _headerbar_disconnected(self):
self.headerbar_button_connect.handler_block_by_func( self._headerbar_connection_button_active = False
self.on_headerbar_connection_active_notify
)
self.headerbar_button_connect.set_active(False) self.headerbar_button_connect.set_active(False)
self.headerbar_button_connect.set_state(False) self.headerbar_button_connect.set_state(False)
self.headerbar_button_connect.handler_unblock_by_func( self._headerbar_connection_button_active = True
self.on_headerbar_connection_active_notify
)
self.headerbar_title_stack.set_visible_child(self.headerbar_connection_label)
def _set_headerbar_sensitive(self, sensitive, connecting): def _set_headerbar_sensitive(self, sensitive, connecting):
@ -655,6 +620,4 @@ class Window(Gtk.ApplicationWindow):
def _show_error(self, message): def _show_error(self, message):
self.info_bar.set_message_type(Gtk.MessageType.ERROR) self.info_toast.add_toast(Adw.Toast.new(message))
self.info_label.set_text(message)
self.info_revealer.set_reveal_child(True)