Vala开发中Gtk.Box子类的enter_notify_event事件无法正常触发如何解决
问题排查
你的代码有两个核心问题导致enter/leave事件不生效:
Gtk.Box默认是无窗口控件,没有属于自己的GdkWindow,所有输入事件默认只会发送到有窗口的控件上,你就算加了事件掩码也收不到事件。- 你同时重写了
enter_notify_event/leave_notify_event虚函数,又手动连接了同名信号,属于重复逻辑,两者留一个即可。
修复方案
方案1:使用EventBox捕获事件(最稳妥)
Gtk.EventBox是专门用来给无窗口控件提供事件捕获能力的容器,修改逻辑如下:
public class EpisodeListItem : Gtk.Box { // Data public Episode episode; // Widgets public Gtk.Box buttons_box; public Gtk.Button download_button; public Gtk.Button play_button; public Gtk.Button delete_button; public Gtk.Label title; // 新增EventBox用于捕获事件 private Gtk.EventBox event_container; // Constructors public EpisodeListItem (Episode episode) { info ("%s: %s", episode.title, episode.current_download_status.to_string ()); orientation = Gtk.Orientation.HORIZONTAL; halign = Gtk.Align.FILL; this.episode = episode; // 初始化事件容器,配置事件掩码 event_container = new Gtk.EventBox(); event_container.add_events( Gdk.EventMask.ENTER_NOTIFY_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK ); add(event_container); // 内部容器放所有内容控件 var content_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); event_container.add(content_box); title = new Gtk.Label (episode.title) { expand = true, halign = Gtk.Align.START }; content_box.add (title); download_button = new Gtk.Button.from_icon_name ( "browser-download-symbolic", Gtk.IconSize.BUTTON ); play_button = new Gtk.Button.from_icon_name ( "media-playback-start-symbolic", Gtk.IconSize.BUTTON ); delete_button = new Gtk.Button.from_icon_name ( "edit-delete-symbolic", Gtk.IconSize.BUTTON ); buttons_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 5) { no_show_all = true }; if (this.episode.current_download_status == DownloadStatus.NOT_DOWNLOADED) { buttons_box.pack_start (download_button); } else if (this.episode.current_download_status == DownloadStatus.DOWNLOADED){ buttons_box.pack_start (play_button); } buttons_box.pack_end (delete_button); content_box.add (buttons_box); // 事件绑定到EventBox上,删除原来的虚函数重写和重复信号绑定 event_container.enter_notify_event.connect ((e) => { info ("Episode List Item entered"); buttons_box.show (); return false; }); event_container.leave_notify_event.connect ((e) => { // 过滤鼠标进入子控件触发的假离开事件 if (e.detail == Gdk.NotifyType.INFERIOR) { return false; } buttons_box.hide (); return false; }); } }
方案2:用CSS实现(更简单,推荐)
完全不用写事件逻辑,用GTK的CSS伪类实现悬停显示,还可以加过渡效果更丝滑:
- 给控件添加CSS标识:
// 在EpisodeListItem构造函数中添加 this.set_css_name("episode-list-item"); buttons_box.get_style_context().add_class("hover-buttons");
- 加载全局CSS规则:
.hover-buttons { opacity: 0; transition: opacity 0.2s ease; } episode-list-item:hover .hover-buttons { opacity: 1; }
内容的提问来源于stack exchange,提问作者Christopher Leggett
相关产品推荐
相关产品推荐

