diff --git a/src/audrey.gresource.xml b/src/audrey.gresource.xml index 9fd414e..f676199 100644 --- a/src/audrey.gresource.xml +++ b/src/audrey.gresource.xml @@ -5,6 +5,7 @@ gtk/help-overlay.ui ui/play_queue.ui ui/play_queue_song.ui + ui/playbar.ui ui/setup.ui ui/window.ui diff --git a/src/meson.build b/src/meson.build index 4704f2e..d35c67e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,6 +6,7 @@ audrey_sources = [ 'playbin.vala', 'subsonic.vala', 'ui/play_queue.vala', + 'ui/playbar.vala', 'ui/setup.vala', 'ui/window.vala', ] diff --git a/src/ui/meson.build b/src/ui/meson.build index a0ba2ba..e59fe29 100644 --- a/src/ui/meson.build +++ b/src/ui/meson.build @@ -1,7 +1,7 @@ blueprints = custom_target( 'blueprints', - input: files('play_queue.blp', 'play_queue_song.blp', 'setup.blp', 'window.blp'), - output: ['play_queue.ui', 'play_queue_song.ui', 'setup.ui', 'window.ui'], + input: files('play_queue.blp', 'play_queue_song.blp', 'playbar.blp', 'setup.blp', 'window.blp'), + output: ['play_queue.ui', 'play_queue_song.ui', 'playbar.ui', 'setup.ui', 'window.ui'], command: [ find_program('blueprint-compiler'), 'batch-compile', diff --git a/src/ui/playbar.blp b/src/ui/playbar.blp new file mode 100644 index 0000000..6f9e3b5 --- /dev/null +++ b/src/ui/playbar.blp @@ -0,0 +1,154 @@ +using Gtk 4.0; + +template $UiPlaybar: Box { + styles [ + "toolbar", + ] + + CenterBox { + [start] + Box { + orientation: vertical; + valign: center; + + Label { + styles [ "heading" ] + xalign: 0; + halign: start; + label: bind $song_title (template.song) as ; + ellipsize: end; + } + + Label { + styles [ "caption" ] + xalign: 0; + label: bind $song_artist (template.song) as ; + ellipsize: end; + } + + Label { + styles [ "caption" ] + xalign: 0; + label: bind $song_album (template.song) as ; + ellipsize: end; + } + } + + [center] + Box { + orientation: vertical; + halign: center; + hexpand: true; + + CenterBox { + [start] + Label play_position_label { + styles [ + "caption", + "numeric", + ] + + label: bind $format_timestamp (template.playbin as <$Playbin>.position) as ; + } + + [center] + Scale play_position { + name: "seek-scale"; + orientation: horizontal; + width-request: 400; + sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; + + adjustment: Adjustment { + lower: 0; + value: bind template.playbin as <$Playbin>.position; + upper: bind template.playbin as <$Playbin>.duration; + }; + + change-value => $on_play_position_seek (); + } + + [end] + Label play_duration { + styles [ + "caption", + "numeric", + ] + + label: bind $format_timestamp (template.playbin as <$Playbin>.duration) as ; + } + } + + Box { + halign: center; + orientation: horizontal; + + Button { + icon-name: "media-skip-backward"; + valign: center; + sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; + + clicked => $on_skip_backward_clicked (); + } + + Button { + icon-name: "media-seek-backward"; + valign: center; + sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; + + clicked => $seek_backward (); + } + + Button { + icon-name: bind $play_pause_icon_name (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; + valign: center; + sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; + + clicked => $on_play_pause_clicked (); + } + + Button { + icon-name: "media-seek-forward"; + valign: center; + sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; + + clicked => $seek_forward (); + } + + Button { + icon-name: "media-skip-forward"; + valign: center; + sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; + + clicked => $on_skip_forward_clicked (); + } + } + } + + [end] + Box { + Button { + icon-name: "non-starred"; + valign: center; + } + + Button { + icon-name: bind $mute_button_icon_name (template.playbin as <$Playbin>.mute) as ; + valign: center; + + clicked => $on_mute_toggle (); + } + + Scale { + name: "volume-scale"; + orientation: horizontal; + width-request: 130; + + adjustment: Adjustment { + lower: 0; + value: bind template.volume bidirectional; + upper: 100; + }; + } + } + } +} diff --git a/src/ui/playbar.vala b/src/ui/playbar.vala new file mode 100644 index 0000000..1f9bd28 --- /dev/null +++ b/src/ui/playbar.vala @@ -0,0 +1,80 @@ +[GtkTemplate (ui = "/eu/callcc/audrey/ui/playbar.ui")] +class Ui.Playbar : Gtk.Box { + public Subsonic.Song? song { get; set; } + public Playbin playbin { get; set; } + public int volume { + get { return playbin == null ? 100 : playbin.volume; } + set { playbin.volume = value; } + } + + [GtkCallback] private string format_timestamp (double s) { + return "%02d:%02d".printf (((int) s)/60, ((int) s)%60); + } + + [GtkCallback] private bool on_play_position_seek (Gtk.Range range, Gtk.ScrollType scroll_type, double value) { + this.playbin.seek ((int64) value); + return false; + } + + [GtkCallback] private void on_play_pause_clicked () { + if (this.playbin.state == PlaybinState.PLAYING) { + this.playbin.pause(); + } else { + this.playbin.play(); + } + } + + [GtkCallback] private string play_pause_icon_name (PlaybinState state) { + if (state == PlaybinState.PLAYING) { + return "media-playback-pause"; + } else { + return "media-playback-start"; + } + } + + [GtkCallback] private bool playbin_active (PlaybinState state) { + return state != PlaybinState.STOPPED; + } + + [GtkCallback] private string mute_button_icon_name (bool mute) { + return mute ? "audio-volume-muted" : "audio-volume-high"; + } + + [GtkCallback] private void on_mute_toggle () { + this.playbin.mute = !this.playbin.mute; + } + + [GtkCallback] private void on_skip_forward_clicked () { + this.playbin.next_track (); + } + + [GtkCallback] private void on_skip_backward_clicked () { + this.playbin.prev_track (); + } + + [GtkCallback] private void seek_backward () { + // 10 seconds + double new_position = playbin.position - 10.0; + if (new_position < 0.0) new_position = 0.0; + this.playbin.seek (new_position); + } + + [GtkCallback] private void seek_forward () { + // 10 seconds + double new_position = playbin.position + 10.0; + if (new_position > this.playbin.duration) new_position = this.playbin.duration; + this.playbin.seek (new_position); + } + + [GtkCallback] private string song_title (Subsonic.Song? song) { + return song == null ? "" : song.title; + } + + [GtkCallback] private string song_artist (Subsonic.Song? song) { + return song == null ? "" : song.artist; + } + + [GtkCallback] private string song_album (Subsonic.Song? song) { + return song == null ? "" : song.album; + } +} diff --git a/src/ui/window.blp b/src/ui/window.blp index e52b9c2..0cc223f 100644 --- a/src/ui/window.blp +++ b/src/ui/window.blp @@ -95,156 +95,9 @@ template $UiWindow: Adw.ApplicationWindow { Box {} [bottom-bar] - CenterBox { - styles [ - "toolbar", - ] - - [start] - Box { - orientation: vertical; - valign: center; - - Label { - styles [ "heading" ] - xalign: 0; - halign: start; - label: bind $song_title (template.song) as ; - ellipsize: end; - } - - Label { - styles [ "caption" ] - xalign: 0; - label: bind $song_artist (template.song) as ; - ellipsize: end; - } - - Label { - styles [ "caption" ] - xalign: 0; - label: bind $song_album (template.song) as ; - ellipsize: end; - } - } - - [center] - Box { - orientation: vertical; - halign: center; - hexpand: true; - - CenterBox { - [start] - Label play_position_label { - styles [ - "caption", - "numeric", - ] - - label: bind $format_timestamp (template.playbin as <$Playbin>.position) as ; - } - - [center] - Scale play_position { - name: "seek-scale"; - orientation: horizontal; - width-request: 400; - sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; - - adjustment: Adjustment { - lower: 0; - value: bind template.playbin as <$Playbin>.position; - upper: bind template.playbin as <$Playbin>.duration; - }; - - change-value => $on_play_position_seek (); - } - - [end] - Label play_duration { - styles [ - "caption", - "numeric", - ] - - label: bind $format_timestamp (template.playbin as <$Playbin>.duration) as ; - } - } - - Box { - halign: center; - orientation: horizontal; - - Button { - icon-name: "media-skip-backward"; - valign: center; - sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; - - clicked => $on_skip_backward_clicked (); - } - - Button { - icon-name: "media-seek-backward"; - valign: center; - sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; - - clicked => $seek_backward (); - } - - Button { - icon-name: bind $play_pause_icon_name (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; - valign: center; - sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; - - clicked => $on_play_pause_clicked (); - } - - Button { - icon-name: "media-seek-forward"; - valign: center; - sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; - - clicked => $seek_forward (); - } - - Button { - icon-name: "media-skip-forward"; - valign: center; - sensitive: bind $playbin_active (template.playbin as <$Playbin>.state as <$PlaybinState>) as ; - - clicked => $on_skip_forward_clicked (); - } - } - } - - [end] - Box { - Button { - icon-name: "non-starred"; - valign: center; - } - - Button { - icon-name: bind $mute_button_icon_name (template.mute) as ; - valign: center; - - clicked => $on_mute_toggle (); - } - - Scale { - name: "volume-scale"; - orientation: horizontal; - width-request: 130; - - adjustment: Adjustment { - lower: 0; - value: bind template.volume bidirectional; - upper: 100; - }; - } - } + $UiPlaybar playbar { + song: bind template.song; + playbin: bind template.playbin; } }; } - diff --git a/src/ui/window.vala b/src/ui/window.vala index f499053..87c75cf 100644 --- a/src/ui/window.vala +++ b/src/ui/window.vala @@ -5,6 +5,7 @@ class Ui.Window : Adw.ApplicationWindow { [GtkChild] private unowned Gtk.Stack stack; [GtkChild] public unowned Ui.PlayQueue play_queue; + [GtkChild] public unowned Ui.Playbar playbar; [GtkChild] public unowned Adw.ButtonRow shuffle_all_tracks; private Setup setup; @@ -115,78 +116,7 @@ class Ui.Window : Adw.ApplicationWindow { } } - [GtkCallback] private string format_timestamp (double s) { - return "%02d:%02d".printf (((int) s)/60, ((int) s)%60); - } - - [GtkCallback] private bool on_play_position_seek (Gtk.Range range, Gtk.ScrollType scroll_type, double value) { - this.playbin.seek ((int64) value); - return false; - } - - [GtkCallback] private void on_play_pause_clicked () { - if (this.playbin.state == PlaybinState.PLAYING) { - this.playbin.pause(); - } else { - this.playbin.play(); - } - } - - [GtkCallback] private string play_pause_icon_name (PlaybinState state) { - if (state == PlaybinState.PLAYING) { - return "media-playback-pause"; - } else { - return "media-playback-start"; - } - } - - [GtkCallback] private bool playbin_active (PlaybinState state) { - return state != PlaybinState.STOPPED; - } - - [GtkCallback] private string mute_button_icon_name (bool mute) { - return mute ? "audio-volume-muted" : "audio-volume-high"; - } - - [GtkCallback] private void on_mute_toggle () { - this.mute = !this.mute; - } - - [GtkCallback] private void on_skip_forward_clicked () { - this.playbin.next_track (); - } - - [GtkCallback] private void on_skip_backward_clicked () { - this.playbin.prev_track (); - } - [GtkCallback] private void show_setup_dialog () { this.setup.present (this); } - - [GtkCallback] private void seek_backward () { - // 10 seconds - double new_position = playbin.position - 10.0; - if (new_position < 0.0) new_position = 0.0; - this.playbin.seek (new_position); - } - - [GtkCallback] private void seek_forward () { - // 10 seconds - double new_position = playbin.position + 10.0; - if (new_position > this.playbin.duration) new_position = this.playbin.duration; - this.playbin.seek (new_position); - } - - [GtkCallback] private string song_title (Subsonic.Song? song) { - return song == null ? "" : song.title; - } - - [GtkCallback] private string song_artist (Subsonic.Song? song) { - return song == null ? "" : song.artist; - } - - [GtkCallback] private string song_album (Subsonic.Song? song) { - return song == null ? "" : song.album; - } }