You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vala开发中Gtk.Box子类的enter_notify_event事件无法正常触发如何解决

问题排查

你的代码有两个核心问题导致enter/leave事件不生效:

  1. Gtk.Box 默认是无窗口控件,没有属于自己的GdkWindow,所有输入事件默认只会发送到有窗口的控件上,你就算加了事件掩码也收不到事件。
  2. 你同时重写了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伪类实现悬停显示,还可以加过渡效果更丝滑:

  1. 给控件添加CSS标识:
// 在EpisodeListItem构造函数中添加
this.set_css_name("episode-list-item");
buttons_box.get_style_context().add_class("hover-buttons");
  1. 加载全局CSS规则:
.hover-buttons {
  opacity: 0;
  transition: opacity 0.2s ease;
}
episode-list-item:hover .hover-buttons {
  opacity: 1;
}

内容的提问来源于stack exchange,提问作者Christopher Leggett

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 07:57:04