gtk-rs中GtkGrid控件拖拽移动无效:如何正确移动网格内控件
我正在使用gtk-rs(依赖配置为gtk4 0.11.3、gtk4-layer-shell 0.8.0)开发Gtk应用,尝试通过移除并重新attach的方式移动GtkGrid内的Button控件。实现拖拽逻辑后,拖拽结束时计算偏移量更新控件位置,grid.query_child显示控件位置已成功变更,但UI上该Button始终停留在初始的左上角位置;多次拖拽后,查询到的位置持续更新,但界面无任何变化。
依赖配置
gtk = { version = "0.11.3", package = "gtk4", features = ["v4_22"] } gtk-layer-shell = { version = "0.8.0", package = "gtk4-layer-shell" }
Rust代码
use gtk::glib; use gtk::glib::{CLONE_MACRO_LOG_DOMAIN, clone}; use gtk::{Application, ApplicationWindow, Button, GestureDrag, Grid, prelude::*}; use gtk_layer_shell::{Layer, LayerShell}; use std::cell::{Cell}; use std::env; const CELL_SIZE: i32 = 80; const CELL_GAP: i32 = 16; const COLS: i32 = 4; const ROWS: i32 = 8; fn main() -> gtk::glib::ExitCode { unsafe { env::set_var("G_MESSAGES_DEBUG", CLONE_MACRO_LOG_DOMAIN); env::set_var("G_MESSAGES_DEBUG", "all"); } let application = Application::builder() .application_id("application") .build(); application.connect_activate(build_ui); application.run() } // convert col/row count into width/height fn widget_size(cols: i32, rows: i32) -> (i32, i32) { ( CELL_GAP * (cols - 1) + CELL_SIZE * cols, CELL_GAP * (rows - 1) + CELL_SIZE * rows, ) } // build window fn build_ui(app: &Application) { let window = ApplicationWindow::builder() .application(app) .can_focus(false) .title("Grid Layout") .build(); // doesn't matter here, the problem is no different with or without layer shell window.init_layer_shell(); window.set_layer(Layer::Top); // calculate a 4x8 grid widget size, // that is (4 cols + 3 gaps) x (8 rows + 7 gaps) let (grid_width, grid_height) = widget_size(4, 8); let grid = Grid::builder() .row_spacing(CELL_GAP) .column_spacing(CELL_GAP) .width_request(grid_width) .height_request(grid_height) .build(); let button = Button::builder() .label("drag") .width_request(CELL_SIZE) .height_request(CELL_SIZE) .build(); // button position let position = Cell::new((0, 0)); let (x, y) = position.get(); // the original position grid.attach(&button, x, y, 1, 1); let drag = GestureDrag::new(); // on drag end, calculates how many col/row should offset from relative position drag.connect_drag_end(clone!( #[weak] button, #[weak] grid, move |_, offset_x, offset_y| { let (c, r, _, _) = grid.query_child(&button); // the original position println!("origin {} {}", c, r); grid.remove(&button); let x_movement = (offset_x / (CELL_SIZE + CELL_GAP) as f64) as i32; let y_movement = (offset_y / (CELL_SIZE + CELL_GAP) as f64) as i32; println!("offset {} {}", x_movement, y_movement); position.update(|(x, y)| (x + x_movement, y + y_movement)); // cache the position let (x, y) = position.get(); println!("new {} {}", x, y); // new position grid.attach(&button, x, y, 1, 1); let (c, r, _, _) = grid.query_child(&button); // confirm result of re-attaching println!("result {} {}", c, r); grid.queue_draw(); } )); button.add_controller(drag); window.set_child(Some(&grid)); window.present(); }
拖拽运行日志
第一次拖拽:
origin 0 0 offset 0 1 new 0 1 result 0 1
第二次拖拽:
origin 0 1 offset 0 1 new 0 2 result 0 2
解决方案
问题出在移除控件后重新attach时,没有让Grid重新计算布局。虽然grid.query_child显示位置已更新,但Gtk的布局系统没有触发重排,导致UI不刷新。
方案1:触发Grid重新计算布局
queue_draw()仅负责重绘控件像素,不会触发布局计算。改用queue_resize()通知布局系统重新计算所有子控件的位置,从而更新UI。
修改拖拽结束回调的最后一行:
// 替换原有的 grid.queue_draw(); grid.queue_resize();
方案2:直接修改控件的Grid属性(推荐)
无需移除再重新添加Button,直接通过set_child_property更新控件在Grid中的列和行位置,布局系统会自动触发更新,这是更高效的方式:
修改拖拽结束回调中的代码,移除grid.remove(&button)和grid.attach(&button, x, y, 1, 1),替换为:
grid.set_child_property(&button, "column", x).unwrap(); grid.set_child_property(&button, "row", y).unwrap();
优化后的完整回调代码:
drag.connect_drag_end(clone!( #[weak] button, #[weak] grid, move |_, offset_x, offset_y| { let (c, r, _, _) = grid.query_child(&button); println!("origin {} {}", c, r); let x_movement = (offset_x / (CELL_SIZE + CELL_GAP) as f64) as i32; let y_movement = (offset_y / (CELL_SIZE + CELL_GAP) as f64) as i32; println!("offset {} {}", x_movement, y_movement); position.update(|(x, y)| (x + x_movement, y + y_movement)); let (x, y) = position.get(); println!("new {} {}", x, y); // 直接更新Grid中的位置属性 grid.set_child_property(&button, "column", x).unwrap(); grid.set_child_property(&button, "row", y).unwrap(); let (c, r, _, _) = grid.query_child(&button); println!("result {} {}", c, r); } ));
内容的提问来源于stack exchange,提问作者BowenDean
相关产品推荐
相关产品推荐

