perl-Gtk2 如何识别ScrolledWindow可视区域内部件及判断可见性
Perl-Gtk2 ScrolledWindow 可视范围内部件识别方法
问题场景
开发Perl-Gtk2程序时构建ScrolledWindow容器,内部嵌套Table、Viewport放置大量纵向排列的子部件(示例为100个Gtk2::Expander可展开项),核心需求为判断指定部件是否处于滚动窗口当前可视视图范围内,而非依赖部件本身的visible属性判断是否对用户可见。
参考实现代码:
my $ItemsCount = 100; my $ScWin = Gtk2::ScrolledWindow->new(undef, undef); $ScWin->set_policy('automatic', 'automatic'); my $Frame = Gtk2::Frame->new('Items'); my $ItemsBox = Gtk2::Table->new(2, $ItemsCount+1, FALSE); my $View = Gtk2::Viewport->new(); $View->add($ItemsBox); $ScWin->add($View); for my $i (0 .. $ItemsCount-1) { my $exp = Gtk2::Expander->new(); $exp ->set_label("Item #$i"); $ItemsBox->attach($exp, 0, 1, $i, $i+1, 'fill', 'fill', 1, 2); } $MainBox ->pack_start ($ScWin , TRUE, TRUE, 0);
核心实现逻辑
通过同坐标系下的矩形交集判定实现可见性判断:
- 从滚动窗口关联的Adjustment对象中,提取可视区域在内容坐标系下的边界值
- 将待判断部件的位置、尺寸转换到同一内容坐标系下,得到部件自身的边界值
- 计算两个矩形是否存在重叠,重叠则说明部件处于可视范围内
操作步骤
- 提取可视区域边界值
直接从Viewport关联的水平、垂直Gtk2::Adjustment对象读取参数,换算出内容坐标系下的可视范围:- 垂直方向:
可视顶部 = 垂直Adjustment的value值,可视底部 = value值 + page_size值 - 水平方向:
可视左侧 = 水平Adjustment的value值,可视右侧 = value值 + page_size值
- 垂直方向:
- 提取待判断部件的边界值
用translate_coordinates方法将部件左上角坐标转换到内容根容器(上述代码中的$ItemsBox)的坐标系下,结合部件自身分配的宽高,得到部件的上下左右边界值。 - 做交集判定
只要垂直方向、水平方向均存在重叠,即可判定部件当前处于可视范围内。
可复用判断函数
sub is_widget_in_view { my ($content_root, $target_widget, $viewport) = @_; # 部件本身被手动隐藏时直接判定为不可见 return 0 unless $target_widget->visible; # 读取垂直方向可视范围参数 my $vadj = $viewport->get_vadjustment; my $view_top = $vadj->value; my $view_bottom = $vadj->value + $vadj->page_size; # 读取水平方向可视范围参数 my $hadj = $viewport->get_hadjustment; my $view_left = $hadj->value; my $view_right = $hadj->value + $hadj->page_size; # 转换部件坐标到内容根容器坐标系 my ($widget_x, $widget_y) = $target_widget->translate_coordinates($content_root, 0, 0); my $widget_w = $target_widget->allocation->width; my $widget_h = $target_widget->allocation->height; my $widget_top = $widget_y; my $widget_bottom = $widget_y + $widget_h; my $widget_left = $widget_x; my $widget_right = $widget_x + $widget_w; # 计算重叠关系 my $v_overlap = ($widget_bottom > $view_top) && ($widget_top < $view_bottom); my $h_overlap = ($widget_right > $view_left) && ($widget_left < $view_right); return ($v_overlap && $h_overlap) ? 1 : 0; }
调用示例:
# 判断索引为5的Expander是否处于可视范围内 my $target_expander = $ItemsBox->get_children->[5]; my $is_visible = is_widget_in_view($ItemsBox, $target_expander, $View);
补充说明
- 如果需要判定部件完全处于可视范围内,只需将重叠判断条件替换为:部件边界完全落在可视区域边界内
- 若在滚动事件刚触发、界面未完成重绘的时机调用,可连接Adjustment的
value-changed信号,在重绘完成后触发判断逻辑,避免读取到未更新的allocation值 - 针对使用
Gtk2::Table纵向排列部件的场景,可直接根据部件的attach行索引和行高快速计算部件位置,无需逐次坐标转换,判断效率更高
内容的提问来源于stack exchange,提问作者Kamil.Khoury
相关产品推荐
相关产品推荐

