mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-08-17 13:03:48 -07:00
binds: add drag_threshold for click/drag isolation (#9839)
--------- Co-authored-by: Leeman <lstrout@enlj.com>
This commit is contained in:
@@ -1326,6 +1326,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
},
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "binds:drag_threshold",
|
||||||
|
.description = "Movement threshold in pixels for window dragging and c/g bind flags. 0 to disable and grab on mousedown.",
|
||||||
|
.type = CONFIG_OPTION_INT,
|
||||||
|
.data = SConfigOptionDescription::SRangeData{0, 0, INT_MAX},
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* xwayland:
|
* xwayland:
|
||||||
|
@@ -651,6 +651,7 @@ CConfigManager::CConfigManager() {
|
|||||||
registerConfigVar("binds:disable_keybind_grabbing", Hyprlang::INT{0});
|
registerConfigVar("binds:disable_keybind_grabbing", Hyprlang::INT{0});
|
||||||
registerConfigVar("binds:window_direction_monitor_fallback", Hyprlang::INT{1});
|
registerConfigVar("binds:window_direction_monitor_fallback", Hyprlang::INT{1});
|
||||||
registerConfigVar("binds:allow_pin_fullscreen", Hyprlang::INT{0});
|
registerConfigVar("binds:allow_pin_fullscreen", Hyprlang::INT{0});
|
||||||
|
registerConfigVar("binds:drag_threshold", Hyprlang::INT{0});
|
||||||
|
|
||||||
registerConfigVar("gestures:workspace_swipe", Hyprlang::INT{0});
|
registerConfigVar("gestures:workspace_swipe", Hyprlang::INT{0});
|
||||||
registerConfigVar("gestures:workspace_swipe_fingers", Hyprlang::INT{3});
|
registerConfigVar("gestures:workspace_swipe_fingers", Hyprlang::INT{3});
|
||||||
@@ -2255,6 +2256,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||||||
bool longPress = false;
|
bool longPress = false;
|
||||||
bool hasDescription = false;
|
bool hasDescription = false;
|
||||||
bool dontInhibit = false;
|
bool dontInhibit = false;
|
||||||
|
bool click = false;
|
||||||
|
bool drag = false;
|
||||||
const auto BINDARGS = command.substr(4);
|
const auto BINDARGS = command.substr(4);
|
||||||
|
|
||||||
for (auto const& arg : BINDARGS) {
|
for (auto const& arg : BINDARGS) {
|
||||||
@@ -2280,6 +2283,12 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||||||
hasDescription = true;
|
hasDescription = true;
|
||||||
} else if (arg == 'p') {
|
} else if (arg == 'p') {
|
||||||
dontInhibit = true;
|
dontInhibit = true;
|
||||||
|
} else if (arg == 'c') {
|
||||||
|
click = true;
|
||||||
|
release = true;
|
||||||
|
} else if (arg == 'g') {
|
||||||
|
drag = true;
|
||||||
|
release = true;
|
||||||
} else {
|
} else {
|
||||||
return "bind: invalid flag";
|
return "bind: invalid flag";
|
||||||
}
|
}
|
||||||
@@ -2291,6 +2300,9 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||||||
if (mouse && (repeat || release || locked))
|
if (mouse && (repeat || release || locked))
|
||||||
return "flag m is exclusive";
|
return "flag m is exclusive";
|
||||||
|
|
||||||
|
if (click && drag)
|
||||||
|
return "flags c and g are mutually exclusive";
|
||||||
|
|
||||||
const int numbArgs = hasDescription ? 5 : 4;
|
const int numbArgs = hasDescription ? 5 : 4;
|
||||||
const auto ARGS = CVarList(value, numbArgs);
|
const auto ARGS = CVarList(value, numbArgs);
|
||||||
|
|
||||||
@@ -2350,7 +2362,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||||||
|
|
||||||
g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER,
|
g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER,
|
||||||
COMMAND, locked, m_szCurrentSubmap, DESCRIPTION, release, repeat, longPress,
|
COMMAND, locked, m_szCurrentSubmap, DESCRIPTION, release, repeat, longPress,
|
||||||
mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription, dontInhibit});
|
mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription, dontInhibit,
|
||||||
|
click, drag});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@@ -229,6 +229,7 @@ bool IHyprLayout::onWindowCreatedAutoGroup(PHLWINDOW pWindow) {
|
|||||||
|
|
||||||
void IHyprLayout::onBeginDragWindow() {
|
void IHyprLayout::onBeginDragWindow() {
|
||||||
const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
|
const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
|
||||||
|
static auto PDRAGTHRESHOLD = CConfigValue<Hyprlang::INT>("binds:drag_threshold");
|
||||||
|
|
||||||
m_iMouseMoveEventCount = 1;
|
m_iMouseMoveEventCount = 1;
|
||||||
m_vBeginDragSizeXY = Vector2D();
|
m_vBeginDragSizeXY = Vector2D();
|
||||||
@@ -240,42 +241,11 @@ void IHyprLayout::onBeginDragWindow() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool WAS_FULLSCREEN = DRAGGINGWINDOW->isFullscreen();
|
// Try to pick up dragged window now if drag_threshold is disabled
|
||||||
if (WAS_FULLSCREEN) {
|
// or at least update dragging related variables for the cursors
|
||||||
Debug::log(LOG, "Dragging a fullscreen window");
|
g_pInputManager->m_bDragThresholdReached = *PDRAGTHRESHOLD <= 0;
|
||||||
g_pCompositor->setWindowFullscreenInternal(DRAGGINGWINDOW, FSMODE_NONE);
|
if (updateDragWindow())
|
||||||
}
|
|
||||||
|
|
||||||
const auto PWORKSPACE = DRAGGINGWINDOW->m_pWorkspace;
|
|
||||||
|
|
||||||
if (PWORKSPACE->m_bHasFullscreenWindow && (!DRAGGINGWINDOW->m_bCreatedOverFullscreen || !DRAGGINGWINDOW->m_bIsFloating)) {
|
|
||||||
Debug::log(LOG, "Rejecting drag on a fullscreen workspace. (window under fullscreen)");
|
|
||||||
g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
DRAGGINGWINDOW->m_bDraggingTiled = false;
|
|
||||||
|
|
||||||
m_vDraggingWindowOriginalFloatSize = DRAGGINGWINDOW->m_vLastFloatingSize;
|
|
||||||
|
|
||||||
if (WAS_FULLSCREEN && DRAGGINGWINDOW->m_bIsFloating) {
|
|
||||||
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
|
|
||||||
*DRAGGINGWINDOW->m_vRealPosition = MOUSECOORDS - DRAGGINGWINDOW->m_vRealSize->goal() / 2.f;
|
|
||||||
} else if (!DRAGGINGWINDOW->m_bIsFloating) {
|
|
||||||
if (g_pInputManager->dragMode == MBIND_MOVE) {
|
|
||||||
DRAGGINGWINDOW->m_vLastFloatingSize = (DRAGGINGWINDOW->m_vRealSize->goal() * 0.8489).clamp(Vector2D{5, 5}, Vector2D{}).floor();
|
|
||||||
changeWindowFloatingMode(DRAGGINGWINDOW);
|
|
||||||
DRAGGINGWINDOW->m_bIsFloating = true;
|
|
||||||
DRAGGINGWINDOW->m_bDraggingTiled = true;
|
|
||||||
|
|
||||||
*DRAGGINGWINDOW->m_vRealPosition = g_pInputManager->getMouseCoordsInternal() - DRAGGINGWINDOW->m_vRealSize->goal() / 2.f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_vBeginDragXY = g_pInputManager->getMouseCoordsInternal();
|
|
||||||
m_vBeginDragPositionXY = DRAGGINGWINDOW->m_vRealPosition->goal();
|
|
||||||
m_vBeginDragSizeXY = DRAGGINGWINDOW->m_vRealSize->goal();
|
|
||||||
m_vLastDragXY = m_vBeginDragXY;
|
|
||||||
|
|
||||||
// get the grab corner
|
// get the grab corner
|
||||||
static auto RESIZECORNER = CConfigValue<Hyprlang::INT>("general:resize_corner");
|
static auto RESIZECORNER = CConfigValue<Hyprlang::INT>("general:resize_corner");
|
||||||
@@ -553,6 +523,7 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
|
const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
|
||||||
|
static auto PDRAGTHRESHOLD = CConfigValue<Hyprlang::INT>("binds:drag_threshold");
|
||||||
|
|
||||||
// Window invalid or drag begin size 0,0 meaning we rejected it.
|
// Window invalid or drag begin size 0,0 meaning we rejected it.
|
||||||
if ((!validMapped(DRAGGINGWINDOW) || m_vBeginDragSizeXY == Vector2D())) {
|
if ((!validMapped(DRAGGINGWINDOW) || m_vBeginDragSizeXY == Vector2D())) {
|
||||||
@@ -560,6 +531,15 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Yoink dragged window here instead if using drag_threshold and it has been reached
|
||||||
|
if (*PDRAGTHRESHOLD > 0 && !g_pInputManager->m_bDragThresholdReached) {
|
||||||
|
if ((m_vBeginDragXY.distanceSq(mousePos) <= std::pow(*PDRAGTHRESHOLD, 2) && m_vBeginDragXY == m_vLastDragXY))
|
||||||
|
return;
|
||||||
|
g_pInputManager->m_bDragThresholdReached = true;
|
||||||
|
if (updateDragWindow())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static auto TIMER = std::chrono::high_resolution_clock::now(), MSTIMER = TIMER;
|
static auto TIMER = std::chrono::high_resolution_clock::now(), MSTIMER = TIMER;
|
||||||
|
|
||||||
const auto SPECIAL = DRAGGINGWINDOW->onSpecialWorkspace();
|
const auto SPECIAL = DRAGGINGWINDOW->onSpecialWorkspace();
|
||||||
@@ -962,3 +942,41 @@ Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) {
|
|||||||
|
|
||||||
return sizePredicted;
|
return sizePredicted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IHyprLayout::updateDragWindow() {
|
||||||
|
const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
|
||||||
|
|
||||||
|
if (g_pInputManager->m_bDragThresholdReached) {
|
||||||
|
if (DRAGGINGWINDOW->isFullscreen()) {
|
||||||
|
Debug::log(LOG, "Dragging a fullscreen window");
|
||||||
|
g_pCompositor->setWindowFullscreenInternal(DRAGGINGWINDOW, FSMODE_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto PWORKSPACE = DRAGGINGWINDOW->m_pWorkspace;
|
||||||
|
|
||||||
|
if (PWORKSPACE->m_bHasFullscreenWindow && (!DRAGGINGWINDOW->m_bCreatedOverFullscreen || !DRAGGINGWINDOW->m_bIsFloating)) {
|
||||||
|
Debug::log(LOG, "Rejecting drag on a fullscreen workspace. (window under fullscreen)");
|
||||||
|
g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DRAGGINGWINDOW->m_bDraggingTiled = false;
|
||||||
|
m_vDraggingWindowOriginalFloatSize = DRAGGINGWINDOW->m_vLastFloatingSize;
|
||||||
|
if (!DRAGGINGWINDOW->m_bIsFloating && g_pInputManager->dragMode == MBIND_MOVE) {
|
||||||
|
DRAGGINGWINDOW->m_vLastFloatingSize = (DRAGGINGWINDOW->m_vRealSize->goal() * 0.8489).clamp(Vector2D{5, 5}, Vector2D{}).floor();
|
||||||
|
*DRAGGINGWINDOW->m_vRealPosition = g_pInputManager->getMouseCoordsInternal() - DRAGGINGWINDOW->m_vRealSize->goal() / 2.f;
|
||||||
|
if (g_pInputManager->m_bDragThresholdReached) {
|
||||||
|
changeWindowFloatingMode(DRAGGINGWINDOW);
|
||||||
|
DRAGGINGWINDOW->m_bIsFloating = true;
|
||||||
|
DRAGGINGWINDOW->m_bDraggingTiled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_vBeginDragXY = g_pInputManager->getMouseCoordsInternal();
|
||||||
|
m_vBeginDragPositionXY = DRAGGINGWINDOW->m_vRealPosition->goal();
|
||||||
|
m_vBeginDragSizeXY = DRAGGINGWINDOW->m_vRealSize->goal();
|
||||||
|
m_vLastDragXY = m_vBeginDragXY;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@@ -204,6 +204,13 @@ class IHyprLayout {
|
|||||||
virtual Vector2D predictSizeForNewWindow(PHLWINDOW pWindow);
|
virtual Vector2D predictSizeForNewWindow(PHLWINDOW pWindow);
|
||||||
virtual Vector2D predictSizeForNewWindowFloating(PHLWINDOW pWindow);
|
virtual Vector2D predictSizeForNewWindowFloating(PHLWINDOW pWindow);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Called to try to pick up window for dragging.
|
||||||
|
Updates drag related variables and floats window if threshold reached.
|
||||||
|
Return true to reject
|
||||||
|
*/
|
||||||
|
virtual bool updateDragWindow();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_iMouseMoveEventCount;
|
int m_iMouseMoveEventCount;
|
||||||
Vector2D m_vBeginDragXY;
|
Vector2D m_vBeginDragXY;
|
||||||
|
@@ -460,6 +460,7 @@ bool CKeybindManager::onKeyEvent(std::any event, SP<IKeyboard> pKeyboard) {
|
|||||||
.modmaskAtPressTime = MODS,
|
.modmaskAtPressTime = MODS,
|
||||||
.sent = true,
|
.sent = true,
|
||||||
.submapAtPress = m_szCurrentSelectedSubmap,
|
.submapAtPress = m_szCurrentSelectedSubmap,
|
||||||
|
.mousePosAtPress = g_pInputManager->getMouseCoordsInternal(),
|
||||||
};
|
};
|
||||||
|
|
||||||
m_vActiveKeybinds.clear();
|
m_vActiveKeybinds.clear();
|
||||||
@@ -551,6 +552,7 @@ bool CKeybindManager::onMouseEvent(const IPointer::SButtonEvent& e) {
|
|||||||
const auto KEY = SPressedKeyWithMods{
|
const auto KEY = SPressedKeyWithMods{
|
||||||
.keyName = KEY_NAME,
|
.keyName = KEY_NAME,
|
||||||
.modmaskAtPressTime = MODS,
|
.modmaskAtPressTime = MODS,
|
||||||
|
.mousePosAtPress = g_pInputManager->getMouseCoordsInternal(),
|
||||||
};
|
};
|
||||||
|
|
||||||
m_vActiveKeybinds.clear();
|
m_vActiveKeybinds.clear();
|
||||||
@@ -638,6 +640,8 @@ std::string CKeybindManager::getCurrentSubmap() {
|
|||||||
|
|
||||||
SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed) {
|
SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed) {
|
||||||
static auto PDISABLEINHIBIT = CConfigValue<Hyprlang::INT>("binds:disable_keybind_grabbing");
|
static auto PDISABLEINHIBIT = CConfigValue<Hyprlang::INT>("binds:disable_keybind_grabbing");
|
||||||
|
static auto PDRAGTHRESHOLD = CConfigValue<Hyprlang::INT>("binds:drag_threshold");
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
SDispatchResult res;
|
SDispatchResult res;
|
||||||
|
|
||||||
@@ -736,6 +740,14 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP
|
|||||||
found = true; // suppress the event
|
found = true; // suppress the event
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Require mouse to stay inside drag_threshold for clicks, outside for drags
|
||||||
|
// Check if either a mouse bind has triggered or currently over the threshold (maybe there is no mouse bind on the same key)
|
||||||
|
const auto THRESHOLDREACHED = key.mousePosAtPress.distanceSq(g_pInputManager->getMouseCoordsInternal()) > std::pow(*PDRAGTHRESHOLD, 2);
|
||||||
|
if (k->click && (g_pInputManager->m_bDragThresholdReached || THRESHOLDREACHED))
|
||||||
|
continue;
|
||||||
|
else if (k->drag && !g_pInputManager->m_bDragThresholdReached && !THRESHOLDREACHED)
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (k->longPress) {
|
if (k->longPress) {
|
||||||
@@ -788,6 +800,8 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP
|
|||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_pInputManager->m_bDragThresholdReached = false;
|
||||||
|
|
||||||
// if keybind wasn't found (or dispatcher said to) then pass event
|
// if keybind wasn't found (or dispatcher said to) then pass event
|
||||||
res.passEvent |= !found;
|
res.passEvent |= !found;
|
||||||
|
|
||||||
|
@@ -39,6 +39,8 @@ struct SKeybind {
|
|||||||
bool multiKey = false;
|
bool multiKey = false;
|
||||||
bool hasDescription = false;
|
bool hasDescription = false;
|
||||||
bool dontInhibit = false;
|
bool dontInhibit = false;
|
||||||
|
bool click = false;
|
||||||
|
bool drag = false;
|
||||||
|
|
||||||
// DO NOT INITIALIZE
|
// DO NOT INITIALIZE
|
||||||
bool shadowed = false;
|
bool shadowed = false;
|
||||||
@@ -62,6 +64,7 @@ struct SPressedKeyWithMods {
|
|||||||
uint32_t modmaskAtPressTime = 0;
|
uint32_t modmaskAtPressTime = 0;
|
||||||
bool sent = false;
|
bool sent = false;
|
||||||
std::string submapAtPress = "";
|
std::string submapAtPress = "";
|
||||||
|
Vector2D mousePosAtPress = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SParsedKey {
|
struct SParsedKey {
|
||||||
|
@@ -149,6 +149,7 @@ class CInputManager {
|
|||||||
PHLWINDOWREF currentlyDraggedWindow;
|
PHLWINDOWREF currentlyDraggedWindow;
|
||||||
eMouseBindMode dragMode = MBIND_INVALID;
|
eMouseBindMode dragMode = MBIND_INVALID;
|
||||||
bool m_bWasDraggingWindow = false;
|
bool m_bWasDraggingWindow = false;
|
||||||
|
bool m_bDragThresholdReached = false;
|
||||||
|
|
||||||
// for refocus to be forced
|
// for refocus to be forced
|
||||||
PHLWINDOWREF m_pForcedFocus;
|
PHLWINDOWREF m_pForcedFocus;
|
||||||
|
Reference in New Issue
Block a user