Port UI to GTK 4

This commit is contained in:
coderkun 2023-01-08 18:20:30 +01:00
commit 8359143663
21 changed files with 1038 additions and 2066 deletions

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,13 @@
font-weight:bold; font-weight:bold;
} }
revealer.sidebar > * { #cover_info_revealer {
background-color:alpha(@theme_bg_color, 0.8); background-color:alpha(@theme_bg_color, 0.8);
box-shadow:0 0 10px @theme_bg_color; box-shadow:0 0 10px @theme_bg_color;
margin-left:20px margin-left:20px;
} }
revealer.sidebar scale mark indicator { #cover_info_revealer scale mark indicator {
margin-right:5px; margin-right:5px;
} }
@ -53,3 +65,6 @@ iconview.view.selection:selected:focus {
iconview.view.selection:hover { iconview.view.selection:hover {
-gtk-icon-effect:none; -gtk-icon-effect:none;
} }
gridview child {
padding: 1px;
}

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="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="sidebar"/>
<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,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,180 @@
<?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">
<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>
<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>
<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">
<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>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkActionBar" id="actionbar_standalone">
<child>
<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>
<child>
<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>
</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,229 +1,74 @@
<?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="GtkBox">
<signal name="window-state-event" handler="on_state" swapped="no"/>
<child>
<object class="GtkOverlay">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkStack" id="content_stack">
<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="GtkStack" id="panel_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="name">page0</property>
<property name="title">page0</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="valign">start</property>
<child>
<object class="GtkInfoBar" id="info_bar">
<property name="can-focus">False</property>
<property name="valign">start</property>
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="baseline-position">top</property> <child>
<property name="show-close-button">True</property> <object class="AdwHeaderBar" id="headerbar">
<signal name="close" handler="on_info_bar_close" swapped="no"/> <property name="centering-policy">strict</property>
<signal name="response" handler="on_info_bar_response" swapped="no"/> <property name="show_end_title_buttons">true</property>
<child internal-child="action_area"> <property name="title-widget">
<object class="GtkButtonBox"> <object class="AdwViewSwitcherTitle" id="headerbar_panel_switcher">
<property name="can-focus">False</property> <property name="title">CoverGrid</property>
</object> <property name="stack">panel_stack</property>
<packing> </object>
<property name="expand">False</property> </property>
<property name="fill">False</property> <child>
<property name="position">0</property> <object class="GtkSwitch" id="headerbar_button_connect">
</packing> <signal name="state-set" handler="on_headerbar_connection_state_set" swapped="no"/>
</object>
</child>
<child>
<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>
<child internal-child="content_area"> <child>
<object class="GtkBox"> <object class="AdwToastOverlay" id="info_toast">
<property name="can-focus">False</property> <child>
<child> <object class="GtkStack" id="content_stack">
<object class="GtkLabel" id="info_label"> <property name="name">content_stack</property>
<property name="visible">True</property> <property name="vexpand">true</property>
<property name="can-focus">False</property> <child>
</object> <object class="AdwViewStack" id="panel_stack">
<packing> <property name="vexpand">true</property>
<property name="expand">False</property> <signal name="notify::visible-child" handler="on_stack_switched" swapped="no"/>
<property name="fill">True</property> </object>
<property name="position">0</property> </child>
</packing> </object>
</child> </child>
</object> </object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child> </child>
</object> <child>
</child> <object class="AdwViewSwitcherBar">
</object> <property name="stack">panel_stack</property>
</child> <binding name="reveal">
</object> <lookup name="title-visible">headerbar_panel_switcher</lookup>
</child> </binding>
<child type="titlebar"> </object>
<object class="GtkHeaderBar" id="headerbar"> </child>
<property name="name">headerbar</property> </object>
<property name="visible">True</property> </property>
<property name="can-focus">False</property> </template>
<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

@ -9,13 +9,9 @@
<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

@ -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,8 +5,9 @@ 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
@ -38,6 +39,7 @@ class Application(Gtk.Application):
self._load_css() self._load_css()
self._setup_actions() self._setup_actions()
self._load_appmenu() self._load_appmenu()
self._setup_adw()
def do_activate(self): def do_activate(self):
@ -70,15 +72,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
) )
@ -102,3 +104,15 @@ class Application(Gtk.Application):
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.ViewSwitcherTitle()
Adw.ViewSwitcherBar()
Adw.ViewStackPage()
Adw.ToastOverlay()
Adw.StatusPage()
Adw.Flap()
Adw.EntryRow()
Adw.PasswordEntryRow()

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,22 +44,20 @@ 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
@ -84,11 +65,11 @@ class CoverPanel(Gtk.Overlay):
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() @Gtk.Template.Callback()
@ -97,10 +78,9 @@ class CoverPanel(Gtk.Overlay):
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() @Gtk.Template.Callback()
@ -144,7 +124,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):
@ -186,7 +166,7 @@ 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 = self._get_default_image()
@ -236,8 +216,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()
@ -245,12 +228,14 @@ class CoverPanel(Gtk.Overlay):
"""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.cover_stack.get_size(Gtk.Orientation.HORIZONTAL)
size = self.cover_scroll.get_allocation() 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 +244,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 +254,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

@ -2,25 +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
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/library-toolbar.ui') @Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/library-panel.ui')
class LibraryToolbar(Gtk.ButtonBox): class LibraryPanel(Adw.Bin):
__gtype_name__ = 'McgLibraryToolbar' __gtype_name__ = 'McgLibraryPanel'
__gsignals__ = { __gsignals__ = {
'select': (GObject.SIGNAL_RUN_FIRST, None, (bool,)), 'select': (GObject.SIGNAL_RUN_FIRST, None, (bool,)),
'toggle-search': (GObject.SIGNAL_RUN_FIRST, None, (bool,)), 'toggle-search': (GObject.SIGNAL_RUN_FIRST, None, (bool,)),
@ -32,6 +34,10 @@ class LibraryToolbar(Gtk.ButtonBox):
} }
# Widgets # Widgets
panel_standalone = Gtk.Template.Child()
actionbar_revealer = Gtk.Template.Child()
# Toolbar
toolbar = Gtk.Template.Child()
select_button = Gtk.Template.Child() select_button = Gtk.Template.Child()
toolbar_search_bar = Gtk.Template.Child() toolbar_search_bar = Gtk.Template.Child()
toolbar_popover = Gtk.Template.Child() toolbar_popover = Gtk.Template.Child()
@ -40,100 +46,6 @@ class LibraryToolbar(Gtk.ButtonBox):
sort_title = Gtk.Template.Child() sort_title = Gtk.Template.Child()
sort_year = Gtk.Template.Child() sort_year = Gtk.Template.Child()
grid_scale = 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')
class LibraryPanel(Gtk.Stack):
__gtype_name__ = 'McgLibraryPanel'
__gsignals__ = {
'open-standalone': (GObject.SIGNAL_RUN_FIRST, None, ()),
'close-standalone': (GObject.SIGNAL_RUN_FIRST, None, ()),
'update': (GObject.SIGNAL_RUN_FIRST, None, ()),
'play': (GObject.SIGNAL_RUN_FIRST, None, (str,)),
'queue': (GObject.SIGNAL_RUN_FIRST, None, (str,)),
'queue-multiple': (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
'item-size-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,)),
'albumart': (GObject.SIGNAL_RUN_FIRST, None, (str,))
}
# Widgets
panel_standalone = Gtk.Template.Child()
actionbar_revealer = 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()
@ -152,8 +64,8 @@ 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 = {}
@ -167,7 +79,7 @@ class LibraryPanel(Gtk.Stack):
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._allocation = (0, 0)
@ -188,11 +100,8 @@ class LibraryPanel(Gtk.Stack):
# Progress Bar # Progress Bar
self.progress_image.set_from_pixbuf(self._get_default_image()) 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_selection = Gtk.MultiSelection.new(self._library_grid_model)
self._library_grid_model.set_sort_column_id(2, self._sort_type)
self._library_grid_filter = self._library_grid_model.filter_new()
self._library_grid_filter.set_visible_func(self.on_filter_visible)
# Library Grid # Library Grid
self.library_grid.set_model(self._library_grid_filter) self.library_grid.set_model(self._library_grid_filter)
self.library_grid.set_pixbuf_column(0) self.library_grid.set_pixbuf_column(0)
@ -205,7 +114,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):
@ -218,17 +127,17 @@ class LibraryPanel(Gtk.Stack):
if new_allocation == self._allocation: if new_allocation == self._allocation:
return return
self._allocation = new_allocation self._allocation = new_allocation
self._toolbar.get_grid_scale().clear_marks() self.grid_scale.clear_marks()
width = widget.get_allocation().width width = widget.get_allocation().width
lower = int(self._toolbar.get_grid_scale().get_adjustment().get_lower()) lower = int(self.grid_scale.get_adjustment().get_lower())
upper = int(self._toolbar.get_grid_scale().get_adjustment().get_upper()) upper = int(self.grid_scale.get_adjustment().get_upper())
countMin = max(int(width / upper), 1) countMin = max(int(width / upper), 1)
countMax = max(int(width / lower), 1) countMax = max(int(width / lower), 1)
for index in range(countMin, countMax): for index in range(countMin, countMax):
pixel = int(width / index) pixel = int(width / index)
pixel = pixel - (2 * int(pixel / 100)) pixel = pixel - (2 * int(pixel / 100))
self._toolbar.get_grid_scale().add_mark( self.grid_scale.add_mark(
pixel, pixel,
Gtk.PositionType.BOTTOM, Gtk.PositionType.BOTTOM,
None None
@ -256,7 +165,7 @@ class LibraryPanel(Gtk.Stack):
def on_toolbar_scale(self, widget, value): def on_toolbar_scale(self, widget, value):
size = math.floor(value) size = math.floor(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
@ -266,7 +175,7 @@ class LibraryPanel(Gtk.Stack):
def on_toolbar_scaled(self, widget, value): def on_toolbar_scaled(self, widget, value):
size = round(value) size = round(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 False return False
self.emit('item-size-changed', size) self.emit('item-size-changed', size)
@ -286,8 +195,8 @@ class LibraryPanel(Gtk.Stack):
@Gtk.Template.Callback() @Gtk.Template.Callback()
def on_filter_bar_notify(self, widget, value): def on_filter_bar_notify(self, widget, value):
if self._toolbar.is_search_active() is not self.filter_bar.get_search_mode(): if self.toolbar_search_bar.get_active() is not self.filter_bar.get_search_mode():
self._toolbar.set_search_active(self.filter_bar.get_search_mode()) self.toolbar_search_bar.set_active(self.filter_bar.get_search_mode())
@Gtk.Template.Callback() @Gtk.Template.Callback()
@ -377,7 +286,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()
@ -496,23 +405,17 @@ class LibraryPanel(Gtk.Stack):
except Exception as e: except Exception as e:
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._grid_pixbufs[album.get_id()] = pixbuf self._grid_pixbufs[album.get_id()] = pixbuf
self._library_grid_model.append([ 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)
@ -632,12 +535,11 @@ class LibraryPanel(Gtk.Stack):
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
) )

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

@ -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, ()),
@ -67,6 +35,10 @@ class PlaylistPanel(Gtk.Stack):
# Widgets # Widgets
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 +51,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,7 +60,7 @@ 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
@ -101,12 +73,10 @@ class PlaylistPanel(Gtk.Stack):
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 = Gtk.MultiSelection.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)
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,7 +84,7 @@ 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):
@ -137,13 +107,13 @@ class PlaylistPanel(Gtk.Stack):
@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_selection_mode() == Gtk.SelectionMode.SINGLE:
@ -248,10 +218,7 @@ class PlaylistPanel(Gtk.Stack):
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,29 +232,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)
self.playlist_grid.thaw_child_notify()
# TODO why set_columns()? # TODO why set_columns()?
#self.playlist_grid.set_columns(len(playlist)) #self.playlist_grid.set_columns(len(playlist))
self._playlist_lock.release() self._playlist_lock.release()
@ -342,12 +302,11 @@ class PlaylistPanel(Gtk.Stack):
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
) )

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,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
@ -86,3 +86,29 @@ class SortOrder:
ARTIST = 0 ARTIST = 0
TITLE = 1 TITLE = 1
YEAR = 2 YEAR = 2
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

View file

@ -2,7 +2,8 @@
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
@ -11,7 +12,7 @@ except:
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 +44,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'
@ -70,27 +71,27 @@ class Window(Gtk.ApplicationWindow):
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())
# Login screen # Login screen
self._connection_panel = ConnectionPanel() self._connection_panel = ConnectionPanel()
self._panels.append(self._connection_panel)
# Server panel # Server panel
self._server_panel = ServerPanel() self._server_panel = ServerPanel()
self._panels.append(self._server_panel) self._panels.append(self._server_panel)
@ -108,19 +109,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(self._server_panel, 'server-panel', locale.gettext("Server"))
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(self._playlist_panel, 'playlist-panel', locale.gettext("Playlist"))
self.panel_stack.add_titled(self._library_panel, 'library-panel', locale.gettext("Library")) self.panel_stack.add_titled(self._library_panel, 'library-panel', locale.gettext("Library"))
# 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 +135,9 @@ 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::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 +180,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()
@ -228,7 +231,7 @@ class Window(Gtk.ApplicationWindow):
def on_menu_toggle_fullscreen(self, action, value): def on_menu_toggle_fullscreen(self, action, value):
self.panel_stack.set_visible_child(self._cover_panel) self.panel_stack.set_visible_child(self.cover_panel_page)
if not self._state.get_property(WindowState.IS_FULLSCREENED): if not self._state.get_property(WindowState.IS_FULLSCREENED):
self.fullscreen() self.fullscreen()
else: else:
@ -236,52 +239,36 @@ 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)
@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(fullscreened is True)
# 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 +277,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
@ -310,13 +291,13 @@ class Window(Gtk.ApplicationWindow):
self._save_visible_panel() self._save_visible_panel()
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())
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
) )
panel.set_selected(panel == self.panel_stack.get_visible_child())
def on_panel_open_standalone(self, panel): def on_panel_open_standalone(self, panel):
self.set_titlebar(panel.get_headerbar_standalone()) self.set_titlebar(panel.get_headerbar_standalone())
@ -450,9 +431,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)
@ -593,58 +572,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 +615,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)