GTK3自定义非覆盖式滚动条首次显示错位、按钮失效如何解决?
问题原因
- 动态修改
GtkScrollbar可见性时,Greybird主题附带的滚动条过渡动画会干扰GTK的布局重排流程,在notify::page-size同步回调中直接修改控件可见属性时,GTK正在处理调整器的属性变更,没有正确触发外层水平GtkBox的重新布局计算,导致滚动条临时被排到了内容区域下方。 - 只有当用户触发窗口交互时,GTK才会重新走全量布局校验流程,所以交互后布局才会恢复正常。
解决方案
方案1(最优,无需自行实现滚动逻辑)
无需自行实现外置滚动条,GTK自带的GtkScrolledWindow本身就支持非叠加的滚动条模式,只需要关闭叠加滚动属性即可,滚动条会自动占用布局空间不会遮挡内容,完全规避当前遇到的bug:
// 创建滚动窗口后添加这行代码即可 gtk_scrolled_window_set_overlay_scrolling (GTK_SCROLLED_WINDOW (scrolled_window), FALSE); // 滚动条策略直接用默认的GTK_POLICY_AUTOMATIC即可,不用设置EXTERNAL gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
该方案直接使用GTK原生逻辑,稳定性最高,也适配所有主题。
方案2(保留现有自定义滚动逻辑的修复方法)
如果一定要保留现有实现,修改adj_page_sizechanged回调,修改可见性后主动触发外层容器的布局重算即可:
gboolean adj_page_sizechanged (GtkAdjustment *adjustment, GParamSpec *pspec, // notify信号第二个参数为GParamSpec,原代码参数定义错误也可能引发异常 GtkWidget *root_box) // 将外层水平GtkBox作为用户数据传入,不要仅传滚动条 { GtkWidget *scrollbar = g_object_get_data (G_OBJECT (root_box), "custom-scrollbar"); gdouble maxValue = gtk_adjustment_get_upper (adjustment); gdouble maxSize = gtk_adjustment_get_page_size (adjustment); gboolean show = (maxValue - maxSize >= 0.000001); g_object_set (scrollbar, "visible", show, NULL); // 主动触发重排 gtk_widget_queue_resize (root_box); return FALSE; }
同时创建滚动条的时候提前隐藏,并且设置no-show-all属性避免gtk_widget_show_all将其默认显示出来:
GtkWidget * scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, adj_v); gtk_widget_set_no_show_all (scrollbar, TRUE); gtk_widget_hide (scrollbar); g_object_set_data (G_OBJECT (root), "custom-scrollbar", scrollbar); // 绑定信号的时候把root作为用户数据传入 g_signal_connect(adj_v, "notify::page-size", G_CALLBACK (adj_page_sizechanged), root);
方案3(主题兼容补丁)
如果是Greybird主题的动画导致的问题,可以给滚动条强制禁用CSS过渡,在程序初始化的时候添加自定义CSS:
// gtk_init执行完成后添加 GtkCssProvider *provider = gtk_css_provider_new (); gtk_css_provider_load_from_data (provider, "scrollbar { transition: none; }", -1, NULL); gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); g_object_unref (provider);
内容的提问来源于stack exchange,提问作者DarkTrick
相关产品推荐
相关产品推荐

