diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/DragManager.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/DragManager.java
new file mode 100644
index 0000000..462be26
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/DragManager.java
@@ -0,0 +1,77 @@
+/*
+ * Progressia
+ * Copyright (C)  2020-2021  Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ */
+package ru.windcorp.progressia.client.graphics.gui;
+
+import java.util.Objects;
+
+import glm.vec._2.d.Vec2d;
+import ru.windcorp.progressia.client.graphics.gui.event.DragEvent;
+import ru.windcorp.progressia.client.graphics.gui.event.DragStartEvent;
+import ru.windcorp.progressia.client.graphics.gui.event.DragStopEvent;
+import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent;
+import ru.windcorp.progressia.client.graphics.input.KeyEvent;
+import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
+import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
+
+public class DragManager {
+	
+	private Component component;
+	
+	private boolean isDragged = false;
+	private final Vec2d change = new Vec2d();
+	
+	public void install(Component c) {
+		Objects.requireNonNull(c, "c");
+		if (c == component) {
+			return;
+		}
+		if (component != null) {
+			throw new IllegalStateException("Already installed on " + component + "; attempted to install on " + c);
+		}
+		
+		component = c;
+		
+		c.addInputListener(CursorMoveEvent.class, this::onCursorMove, InputBus.Option.ALWAYS);
+		c.addKeyListener(KeyMatcher.LMB, this::onLMB, InputBus.Option.ALWAYS, InputBus.Option.IGNORE_ACTION);
+	}
+	
+	private void onCursorMove(CursorMoveEvent e) {
+		if (isDragged) {
+			Vec2d currentChange = e.getChange(null);
+			change.add(currentChange);
+			component.dispatchEvent(new DragEvent(component, currentChange, change));
+		}
+	}
+	
+	private void onLMB(KeyEvent e) {
+		if (isDragged && e.isRelease()) {
+			
+			isDragged = false;
+			component.dispatchEvent(new DragStopEvent(component, change));
+			
+		} else if (!isDragged && !e.isConsumed() && e.isPress() && component.isHovered()) {
+			
+			isDragged = true;
+			change.set(0, 0);
+			component.dispatchEvent(new DragStartEvent(component));
+			e.consume();
+			
+		}
+	}
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/event/DragEvent.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/event/DragEvent.java
new file mode 100644
index 0000000..74c91ae
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/event/DragEvent.java
@@ -0,0 +1,58 @@
+/*
+ * Progressia
+ * Copyright (C)  2020-2021  Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ */
+package ru.windcorp.progressia.client.graphics.gui.event;
+
+import glm.vec._2.d.Vec2d;
+import ru.windcorp.progressia.client.graphics.gui.Component;
+
+public class DragEvent extends ComponentEvent {
+	
+	private final Vec2d currentChange = new Vec2d();
+	private final Vec2d totalChange = new Vec2d();
+
+	public DragEvent(Component component, Vec2d currentChange, Vec2d totalChange) {
+		super(component);
+		this.currentChange.set(currentChange.x, currentChange.y);
+		this.totalChange.set(totalChange.x, totalChange.y);
+	}
+	
+	public Vec2d getCurrentChange() {
+		return currentChange;
+	}
+	
+	public double getCurrentChangeX() {
+		return currentChange.x;
+	}
+	
+	public double getCurrentChangeY() {
+		return currentChange.y;
+	}
+	
+	public Vec2d getTotalChange() {
+		return totalChange;
+	}
+	
+	public double getTotalChangeX() {
+		return totalChange.x;
+	}
+	
+	public double getTotalChangeY() {
+		return totalChange.y;
+	}
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/event/DragStartEvent.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/event/DragStartEvent.java
new file mode 100644
index 0000000..232b02a
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/event/DragStartEvent.java
@@ -0,0 +1,28 @@
+/*
+ * Progressia
+ * Copyright (C)  2020-2021  Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ */
+package ru.windcorp.progressia.client.graphics.gui.event;
+
+import ru.windcorp.progressia.client.graphics.gui.Component;
+
+public class DragStartEvent extends ComponentEvent {
+
+	public DragStartEvent(Component component) {
+		super(component);
+	}
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/event/DragStopEvent.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/event/DragStopEvent.java
new file mode 100644
index 0000000..bc23628
--- /dev/null
+++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/event/DragStopEvent.java
@@ -0,0 +1,44 @@
+/*
+ * Progressia
+ * Copyright (C)  2020-2021  Wind Corporation and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see .
+ */
+package ru.windcorp.progressia.client.graphics.gui.event;
+
+import glm.vec._2.d.Vec2d;
+import ru.windcorp.progressia.client.graphics.gui.Component;
+
+public class DragStopEvent extends ComponentEvent {
+
+	private final Vec2d totalChange = new Vec2d();
+
+	public DragStopEvent(Component component, Vec2d totalChange) {
+		super(component);
+		this.totalChange.set(totalChange.x, totalChange.y);
+	}
+	
+	public Vec2d getTotalChange() {
+		return totalChange;
+	}
+	
+	public double getTotalChangeX() {
+		return totalChange.x;
+	}
+	
+	public double getTotalChangeY() {
+		return totalChange.y;
+	}
+
+}
diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/input/CursorMoveEvent.java b/src/main/java/ru/windcorp/progressia/client/graphics/input/CursorMoveEvent.java
index c87fc62..ff7ea82 100644
--- a/src/main/java/ru/windcorp/progressia/client/graphics/input/CursorMoveEvent.java
+++ b/src/main/java/ru/windcorp/progressia/client/graphics/input/CursorMoveEvent.java
@@ -18,7 +18,6 @@
  
 package ru.windcorp.progressia.client.graphics.input;
 
-import glm.vec._2.Vec2;
 import glm.vec._2.d.Vec2d;
 
 public class CursorMoveEvent extends CursorEvent {
@@ -81,7 +80,10 @@ public class CursorMoveEvent extends CursorEvent {
 		return getNewY() - getPreviousY();
 	}
 
-	public Vec2 getChange(Vec2 result) {
+	public Vec2d getChange(Vec2d result) {
+		if (result == null) {
+			result = new Vec2d();
+		}
 		return result.set(getChangeX(), getChangeY());
 	}