Skip to content

Commit 60bbb8d

Browse files
committed
Enhance song entry and application logic for improved playback indicators and song highlighting
1 parent 08b1e82 commit 60bbb8d

File tree

5 files changed

+184
-52
lines changed

5 files changed

+184
-52
lines changed

src/Widgets/album-page.vala

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,14 @@ namespace Victrola {
5454
private ListStore album_store;
5555
private ListStore current_album_songs;
5656
private AlbumItem? current_album;
57+
private HashTable<int, SongEntry> song_entries;
5758

5859
construct {
5960
app = (Application) GLib.Application.get_default ();
6061

6162
album_store = new ListStore (typeof (AlbumItem));
6263
current_album_songs = new ListStore (typeof (Song));
64+
song_entries = new HashTable<int, SongEntry> (direct_hash, direct_equal);
6365

6466
build_ui ();
6567
connect_signals ();
@@ -80,6 +82,11 @@ namespace Victrola {
8082

8183
main_stack.visible_child_name = "albums";
8284
this.child = main_stack;
85+
86+
// Update playing indicators when stack page changes
87+
main_stack.notify["visible-child-name"].connect (() => {
88+
update_playing_indicators ();
89+
});
8390
}
8491

8592
private Gtk.Widget build_albums_page () {
@@ -208,11 +215,23 @@ namespace Victrola {
208215

209216
factory.bind.connect ((item) => {
210217
var list_item = (Gtk.ListItem) item;
218+
if (!(list_item.item is Song))
219+
return;
211220
var song = (Song) list_item.item;
212221
var entry = (SongEntry) list_item.child;
213222

214-
entry.playing = list_item.position == app.current_item;
223+
entry.playing = app.is_current_song (song);
215224
entry.update (song, SortMode.ALBUM);
225+
226+
// Store reference to this entry
227+
song_entries.set ((int) list_item.position, entry);
228+
});
229+
230+
factory.unbind.connect ((item) => {
231+
var list_item = (Gtk.ListItem) item;
232+
var entry = (SongEntry) list_item.child;
233+
entry.playing = false;
234+
song_entries.remove ((int) list_item.position);
216235
});
217236

218237
song_list_view = new Gtk.ListView (new Gtk.NoSelection (current_album_songs), factory);
@@ -331,6 +350,7 @@ namespace Victrola {
331350

332351
current_album_songs.splice (0, 0, sorted_songs.data);
333352
main_stack.visible_child_name = "album-detail";
353+
update_playing_indicators ();
334354
}
335355

336356
private void on_song_activated (uint position) {
@@ -343,25 +363,28 @@ namespace Victrola {
343363
for (uint i = 0; i < song_count; i++) {
344364
var main_song = (Song) app.song_list.get_item (i);
345365
if (main_song.uri == song.uri) {
346-
app.current_item = (int) i;
366+
app.play_item ((int) i);
367+
update_playing_indicators ();
347368
break;
348369
}
349370
}
350371
}
351372

352-
private void update_playing_indicators () {
353-
// Update playing indicators in both views
354-
if (album_grid_view.model != null) {
355-
var count = album_grid_view.model.get_n_items ();
356-
for (uint i = 0; i < count; i++) {
357-
album_grid_view.model.items_changed (i, 0, 0);
358-
}
359-
}
360-
361-
if (song_list_view.model != null) {
362-
var count = song_list_view.model.get_n_items ();
363-
for (uint i = 0; i < count; i++) {
364-
song_list_view.model.items_changed (i, 0, 0);
373+
public void update_playing_indicators () {
374+
// Directly update the song entries
375+
var current_song = app.current_song;
376+
if (current_song == null)
377+
return;
378+
379+
// Update all visible song entries
380+
var keys = song_entries.get_keys ();
381+
foreach (var pos in keys) {
382+
var entry = song_entries.get (pos);
383+
if (entry != null) {
384+
var song = (Song?) current_album_songs.get_item ((uint) pos);
385+
if (song != null) {
386+
entry.playing = app.is_current_song (song);
387+
}
365388
}
366389
}
367390
}

src/Widgets/artist-page.vala

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,14 @@ namespace Victrola {
4343
private ListStore artist_store;
4444
private ListStore current_artist_songs;
4545
private ArtistItem? current_artist;
46+
private HashTable<int, SongEntry> song_entries;
4647

4748
construct {
4849
app = (Application) GLib.Application.get_default ();
4950

5051
artist_store = new ListStore (typeof (ArtistItem));
5152
current_artist_songs = new ListStore (typeof (Song));
53+
song_entries = new HashTable<int, SongEntry> (direct_hash, direct_equal);
5254

5355
build_ui ();
5456
connect_signals ();
@@ -69,6 +71,11 @@ namespace Victrola {
6971

7072
main_stack.visible_child_name = "artists";
7173
this.child = main_stack;
74+
75+
// Update playing indicators when stack page changes
76+
main_stack.notify["visible-child-name"].connect (() => {
77+
update_playing_indicators ();
78+
});
7279
}
7380

7481
private Gtk.Widget build_artists_page () {
@@ -155,11 +162,23 @@ namespace Victrola {
155162

156163
factory.bind.connect ((item) => {
157164
var list_item = (Gtk.ListItem) item;
165+
if (!(list_item.item is Song))
166+
return;
158167
var song = (Song) list_item.item;
159168
var entry = (SongEntry) list_item.child;
160169

161-
entry.playing = list_item.position == app.current_item;
170+
entry.playing = app.is_current_song (song);
162171
entry.update (song, SortMode.ARTIST);
172+
173+
// Store reference to this entry
174+
song_entries.set ((int) list_item.position, entry);
175+
});
176+
177+
factory.unbind.connect ((item) => {
178+
var list_item = (Gtk.ListItem) item;
179+
var entry = (SongEntry) list_item.child;
180+
entry.playing = false;
181+
song_entries.remove ((int) list_item.position);
163182
});
164183

165184
song_list_view = new Gtk.ListView (new Gtk.NoSelection (current_artist_songs), factory);
@@ -236,25 +255,28 @@ namespace Victrola {
236255
for (uint i = 0; i < song_count; i++) {
237256
var main_song = (Song) app.song_list.get_item (i);
238257
if (main_song.uri == song.uri) {
239-
app.current_item = (int) i;
258+
app.play_item ((int) i);
259+
update_playing_indicators ();
240260
break;
241261
}
242262
}
243263
}
244264

245-
private void update_playing_indicators () {
246-
// Update playing indicators in both views
247-
if (artist_list_view.model != null) {
248-
var count = artist_list_view.model.get_n_items ();
249-
for (uint i = 0; i < count; i++) {
250-
artist_list_view.model.items_changed (i, 0, 0);
251-
}
252-
}
253-
254-
if (song_list_view.model != null) {
255-
var count = song_list_view.model.get_n_items ();
256-
for (uint i = 0; i < count; i++) {
257-
song_list_view.model.items_changed (i, 0, 0);
265+
public void update_playing_indicators () {
266+
// Directly update the song entries
267+
var current_song = app.current_song;
268+
if (current_song == null)
269+
return;
270+
271+
// Update all visible song entries
272+
var keys = song_entries.get_keys ();
273+
foreach (var pos in keys) {
274+
var entry = song_entries.get (pos);
275+
if (entry != null) {
276+
var song = (Song?) current_artist_songs.get_item ((uint) pos);
277+
if (song != null) {
278+
entry.playing = app.is_current_song (song);
279+
}
258280
}
259281
}
260282
}

src/Widgets/song-entry.vala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace Victrola {
2222
picon = new Gtk.Image ();
2323
picon.icon_name = "media-playback-start-symbolic";
2424
picon.pixel_size = 24;
25+
picon.margin_end = 12;
2526
picon.visible = false;
2627
picon.set_parent (this);
2728

src/application.vala

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ namespace Victrola {
4545
private MprisPlayer? _mpris = null;
4646
private uint _mpris_id = 0;
4747
private MainWindow window;
48+
private bool _pending_autoplay = false;
4849

4950
public signal void loading_changed (bool loading, uint size);
5051
public signal void index_changed (int index, uint size);
@@ -211,30 +212,60 @@ namespace Victrola {
211212
set {
212213
var count = _song_list.get_n_items ();
213214
value = value < count ? value : 0;
214-
var playing = _current_song != null;
215+
var was_playing = _player.state == Gst.State.PLAYING;
215216
var song = _song_list.get_item (value) as Song;
217+
var song_changed_flag = false;
218+
var item_changed_flag = false;
219+
216220
if (song != null && _current_song != song) {
217221
_current_song = song;
218222
_player.uri = ((!) song).uri;
219223
song_changed ((!) song);
224+
song_changed_flag = true;
220225
}
221226
if (_current_item != value) {
222227
var old_item = _current_item;
223228
_current_item = value;
224-
_song_list.items_changed (old_item, 0, 0);
225-
_song_list.items_changed (value, 0, 0);
229+
if (old_item >= 0 && old_item < count) {
230+
_song_list.items_changed (old_item, 1, 1);
231+
}
232+
if (value >= 0 && value < count) {
233+
_song_list.items_changed (value, 1, 1);
234+
}
226235
index_changed (value, count);
236+
item_changed_flag = true;
237+
}
238+
if (song_changed_flag || item_changed_flag) {
239+
var should_play = was_playing || _pending_autoplay;
240+
var target_state = should_play ? Gst.State.PLAYING : Gst.State.PAUSED;
241+
if (_player.state != target_state) {
242+
_player.state = target_state;
243+
}
227244
}
228-
_player.state = playing ? Gst.State.PLAYING : Gst.State.PAUSED;
229245
}
230246
}
231247

248+
public void play_item (int value) {
249+
_pending_autoplay = true;
250+
current_item = value;
251+
_pending_autoplay = false;
252+
}
253+
232254
public Song? current_song {
233255
get {
234256
return _current_song;
235257
}
236258
}
237259

260+
public bool is_current_song (Song? candidate) {
261+
if (candidate == null || _current_song == null)
262+
return false;
263+
if (candidate == _current_song)
264+
return true;
265+
var current = (!) _current_song;
266+
return candidate.uri == current.uri;
267+
}
268+
238269
public GstPlayer player {
239270
get {
240271
return _player;

0 commit comments

Comments
 (0)