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

Gtkmm3:实现Gtk::ScrolledWindow内画布的鼠标拖拽移动方法

实现Gtk::ScrolledWindow画布拖拽滚动

有没有预定义处理器?

没有直接可用的预定义处理器一键启用画布拖拽滚动功能,需要通过手动处理鼠标事件并调整滚动窗口的Adjustment来实现。

手动实现步骤

要实现拖拽画布并同步滚动条位置,核心是通过鼠标事件计算偏移,然后修改Gtk::ScrolledWindow的水平/垂直Adjustment值:

  • 记录拖拽起始状态:鼠标按下时,记录当前鼠标的全局坐标,以及滚动条的初始偏移值
  • 计算拖拽偏移:鼠标移动时,计算鼠标的位移量,反向调整滚动条的偏移(鼠标向右移动,画布需向左偏移,因此滚动条值要减少)
  • 结束拖拽:鼠标释放时,重置拖拽状态

修改后的完整代码

#include <gtkmm.h>
#include <goocanvasmm.h>

int main(int argc, char *argv[])
{
    Gtk::Main kit(argc, argv);
    Goocanvas::init("Example", "0.1", argc, argv);

    Gtk::ScrolledWindow sw;
    Gtk::Window win;
    Gtk::VBox hbox;

    sw.set_size_request(100, 100);

    Goocanvas::Canvas m_canvas;
    m_canvas.set_size_request(1000, 500);
    m_canvas.set_bounds(0, 0, 4000, 2000);
    m_canvas.set_scale(0.5);
    // 添加必要的鼠标事件掩码,确保能捕获移动事件
    m_canvas.add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
    Glib::RefPtr<Goocanvas::Item> root = m_canvas.get_root_item();

    Glib::RefPtr<Goocanvas::Rect> m_rect = Goocanvas::Rect::create(0, 0, 60, 60);
    m_rect->property_line_width() = 1.0;
    m_rect->property_stroke_color() = "black";
    m_rect->property_fill_color() = "blue";
    root->add_child(m_rect);

    Glib::RefPtr<Goocanvas::Rect> m_rect2 = Goocanvas::Rect::create(4000-80, 2000-80, 60, 60);
    m_rect2->property_line_width() = 1.0;
    m_rect2->property_stroke_color() = "black";
    m_rect2->property_fill_color() = "red";
    root->add_child(m_rect2);

    sw.add(m_canvas);
    hbox.add(sw);
    win.add(hbox);

    // 拖拽状态变量
    bool is_dragging = false;
    double start_x_root, start_y_root;
    double start_hadj_value, start_vadj_value;

    // 鼠标按下事件:记录起始状态
    m_canvas.signal_button_press_event().connect(
        [&](GdkEventButton* event) {
            if (event->button == 1) {
                auto hadj = sw.get_hadjustment();
                auto vadj = sw.get_vadjustment();
                // 记录滚动条初始偏移
                start_hadj_value = hadj->get_value();
                start_vadj_value = vadj->get_value();
                // 记录鼠标全局坐标(避免窗口内坐标受滚动影响)
                start_x_root = event->x_root;
                start_y_root = event->y_root;
                is_dragging = true;
                // 捕获事件,避免传递给其他控件
                return true;
            }
            return false;
        }
    );

    // 鼠标释放事件:结束拖拽
    m_canvas.signal_button_release_event().connect(
        [&](GdkEventButton* event) {
            if (event->button == 1) {
                is_dragging = false;
                return true;
            }
            return false;
        }
    );

    // 鼠标移动事件:计算偏移并调整滚动条
    m_canvas.signal_motion_notify_event().connect(
        [&](GdkEventMotion* event) {
            if (is_dragging) {
                // 计算鼠标位移
                double delta_x = event->x_root - start_x_root;
                double delta_y = event->y_root - start_y_root;

                auto hadj = sw.get_hadjustment();
                auto vadj = sw.get_vadjustment();
                // 反向调整滚动条值:鼠标向右移,画布左移,滚动条值减少
                hadj->set_value(start_hadj_value - delta_x);
                vadj->set_value(start_vadj_value - delta_y);

                return true;
            }
            return false;
        }
    );

    win.show_all_children();
    Gtk::Main::run(win);
}

关键说明

  • Adjustment作用:Gtk::ScrolledWindow的滚动位置由horizontal_adjustment和vertical_adjustment控制,get_value()获取当前偏移量,set_value()设置新偏移,Adjustment会自动处理边界限制(不会超出画布范围)
  • 全局坐标使用:使用x_root/y_root而非窗口内的x/y,避免滚动后窗口内坐标发生变化导致计算错误
  • 事件捕获:将鼠标事件绑定到画布组件(而非滚动窗口),确保拖拽操作在画布区域内生效,同时通过返回true捕获事件,避免传递给父控件

原Makefile无需修改,可直接用于编译测试。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 13:20:40