首页 > 其他分享 >改进NeteaseCloudMusicGtk4:添加移除歌曲按钮

改进NeteaseCloudMusicGtk4:添加移除歌曲按钮

时间:2024-04-09 16:47:14浏览次数:21  
标签:NeteaseCloudMusicGtk4 代码 移除 视图 歌曲 按钮 播放列表

之前已经发了一篇博客简述了如何阅读这个项目,尽管这个项目已经开源很久了,但我找了很久都没有找到怎么从播放列表移除歌曲,那就自己动手实现,再提个 PR 吧。

运行起来应用后通过 Inspector(Ctrl+Shift+I)找到希望放置按钮的位置:专辑按钮的旁边。

第一步就是修改UI文件,把这个按钮显示出来。照葫芦画瓢,先加一个按钮。

<child>
	<object class="GtkButton" id="remove_button">
		<property name="halign">end</property>
		<property name="valign">center</property>
		<property name="icon-name">app-remove-symbolic</property>
		<property name="tooltip-text" translatable="yes">Remove song</property>
		<signal name="clicked" handler="remove_button_clicked_cb" swapped="true" />
		<style>
			<class name="flat" />
		</style>
	</object>
</child>

每一个显示歌曲信息的行通常都有这样几个按钮:艺术家、收藏、专辑。然而在专辑的详细信息里面歌曲的信息就不需要专辑按钮了。行 Widget 是通用的,但是行中的一些 widget 是应该隐藏的。也就是说,移除歌曲按钮应该只在播放列表中才是可见的,移除按钮只显示在下面这个提交中实现:

https://github.com/wngtk/netease-cloud-music-gtk/commit/cb0a143f01c8d02a58b073677b94c14db0c18dfd

原来的代码中有大量的代码拷贝,我添加新的按钮的时候也尽可能的保持其他的地方不变,因此我需要在多处做拷贝粘贴再修改一下字符,导致了两处忘记修改而调试了两个小时。

而单独这一个 commit 中还没有很好的隐藏按钮,因为在代码拷贝的过程中有两处没有改名字,
f3d90c中修改好后这个按钮才显示在合理的地方。

第二步,实现移除正在播放歌曲。几乎所有的按钮的操作最后都是通过发送一个消息来通知 process_action(),因此我们也需要加入一个新的Action

有了新 Action 还只是能响应按钮,真正的业务逻辑还没实现。在这里简述实现的流程:

  1. 如果移除的歌曲是正在播放的歌曲,并且移除后还有歌曲可以播放,那么播放下一曲
  2. 从播放列表中移除想要移除的歌曲
  3. 更新播放列表的展示

流程确实简单,但是在实现的过程中遇到的细节却困扰了我很久。一个主要的问题是数据的更新并不会引发视图的更新。我需要指定调用更新才能更新视图。因为播放列表中完全存了另外一份数据,同样的歌曲列表的视图,和同样歌曲列表,播放列表视图完全拷贝了一份,所以要实现更新且不破坏原来的代码我只能重新初始化页面。

在胡乱加代码以测试到底为什么之前的修改没有失效的时候,还错误的设置了一个不存在的属性,引入了一个 bug,不过在调试器的帮助下轻松定位到崩溃发生的地方,删除不该有的代码便修复了。

总的来说实现过程是曲折的。最后附上一些在改代码的时候的吐槽。

点我看吐槽

因为代码大量使用了异步,调试的时候还是会有一些不方便,没有办法线性的跟踪,只能在处理信号的地方打断点,但是不知道信号是从哪里发起的。我的溯源的方法是查看按钮的回调函数,但是当自己尝试调用原有的 player_controls 的方法,就找不到额外的信号是怎么被发起的,例如我调用 player_controls 里面的 play() 方法播放器会发起一个播放下一曲的信号,但是实际上 play() 方法里面没有做下一曲的事情,那就是其他的注册的信号处理函数导致了播放下一曲,但是一时半会儿找不到具体的问题所在。或许响应按键操作用异步没什么不对,但是现在的代码中整个播放器的业务逻辑也依赖于异步通道(async_channel)传递信息。

因此,在现有的代码中,单纯的给 plyer_controls 指定一个歌曲去 play 不能正确做到,因为其中可能引发其他的异步信号,导致代码不能按照预期执行。这算是有隐藏的控制流。

作者没有使用 MVC,播放器的功能和 UI 是强耦合的,切换下一曲严重依赖图形界面上的按钮的回调函数。要避免意外情况出现,所有的功能,最好通过模拟 UI 操作进行(尽管这样不好,但是原来的代码里面就是这样做的)。尽管所有的异步的信号最终都会汇聚到 process_action() 函数里面来处理,但是如果单纯的操作 Action 往往和期望不一样,因为 Action 执行之前,有一些副作用发生。不通过模拟 UI 操作来实现移除歌曲的功能,实现的效果往往不如人意。例如

很遗憾,原有的代码并没有做视图(View)和数据(Model)的绑定, 数据改变后,视图并不能自动更新。换句话说,在删除播放列表的歌曲的业务逻辑实现后,UI 并没有自动更新。原来的代码用的是 ListBox,

花时间最多的部分是界面的显示,标记正在播放的图标要显示在正确的行,删除了的歌曲应该不再展示,删除播放列表歌曲的过程中导致歌词不显示了。在已有的接口和设计上,播放列表(playlist_lyrics)可能没有考虑过要修改,播放列表就提供的方法有:初始化,更新歌词,切换播放行。

脑袋中想着既然这里没有 MVC, 我就要手动更新视图。因此我尝试直接修改表达视图的对象,可惜的是最终以失败告终,因为一直存在着小问题。或许那种方法的效果是可行的,但是并没有很好的将已有的代码利用起来。因为整个播放器的控制是由 player_controls 和 window 两个对象控制的,有的操作放到 player_controls 里面完成更方便,而为了利用现有的 window 对象已有的方法有的操作放到 window 会更方便。如果严格一点按照设计模式来做,PlyerControl 应该负责所有的数据的控制,不应该有些东西放到了 window 去做。

而移除的歌曲不再显示是通过手动做页面路由,将 Action::ToSongListPage 执行的部分逻辑再执行一次,而不退出当前的播放列表页面。为了避免破坏原有的代码,不修改 Action::ToSongListPage,然而即便是模拟了重新进入页面,依旧会出现没有正确标记正在播放的歌曲。我没有找到问题的根源,因为一切看起来都是正常的,也没有什么头绪去定位问题到底是什么。

最后没有正确标记的解决方法是由 player_controls 移除歌曲后发起一个Action::UpdatePlayListStatus(playlist.get_position(),因为 player_controls 已经有代码使用了 playlist.get_position(),而 UpdatePlayListStatus 又需要一个 position,就只好将这一部分的代码放在 player_controls。相当于是最后又委派给全局来实现。这里的做法来自于下一曲按钮的回调函数会发送 UpdatePlayListStatus。

sender        
	.send_blocking(
		Action::UpdatePlayListStatus(playlist.get_position())
	).unwrap();

标签:NeteaseCloudMusicGtk4,代码,移除,视图,歌曲,按钮,播放列表
From: https://www.cnblogs.com/wngtk/p/18124284

相关文章

  • Android 点击按钮跳转
    packagecom.example.helloworld;importandroidx.appcompat.app.AppCompatActivity;importandroid.content.Intent;importandroid.net.Uri;importandroid.os.Bundle;importandroid.view.View;importandroid.widget.Button;publicclassMainActivityextendsA......
  • 移除链表元素(虚拟节点法、力扣203)
    题目给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val==val 的节点,并返回 新的头节点 。示例1:输入:head=[1,2,6,3,4,5,6],val=6输出:[1,2,3,4,5]示例2:输入:head=[],val=1输出:[]示例3:输入:head=[7,7,7,7],val=......
  • AndroidStudio学习记录(5):图像按钮ImageView的实现
    <?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"......
  • 点击按钮框来选择相应信息(Vue + Java)
    目录前言1.前端2.后端3.总结前言从Java转全栈,对于项目中的功能,从无到有,都会以笔记的形式记录,方便自身的总结以及翻阅原先的知识点参考:java框架零基础从入门到精通的学习路线附开源项目面经等(超全)前后端实现下拉框带条件查询(Vue+Java)通过点击按钮框给予数据选......
  • 加价按钮 (当前价基于起拍价变化)
    效果: <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>价格增加示例<......
  • 链表--移除链表元素--力扣203
    目录题目思路代码细节题目给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val==val 的节点,并返回 新的头节点 。示例1:输入:head=[1,2,6,3,4,5,6],val=6输出:[1,2,3,4,5]示例2:输入:head=[],val=1输出:[]示例3:输入:h......
  • 小米黑鲨helo手机安装应用时没有安装按钮
    情况说明:如图问题的关键点:打包apk的作者放置的图片太大了解决办法:下载ApktoolM快速编辑所要安装的apk文件更换为更小的图片......
  • 移除元素 -- 力扣第27题 -- 暴力、双指针解法
    题目https://leetcode.cn/problems/remove-element/description/给你一个数组nums 和一个值val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用O(1)额外空间并原地修改输入数组。元素的顺序可以改变。你不需......
  • AndroidStudio学习记录(4):单选按钮控件RadioButton
    用于应用二选一等多选选项的设置<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical">&l......
  • AndroidStudio学习记录(3):操纵按钮控件Botton、ImageBotton
    按钮控件是平时看到的,常用Botton和ImageButton控件,一般操纵按钮来实现相应的命令,比如在手机上的查找登录注册,以及点击命令等等。ImaBotton与Button的区别在于它没有文本,只有图片,需要制定图片路径在activity_main.xml文件中,它们是这样使用的:<?xmlversion="1.0"encoding=......