mirror of
https://github.com/hyprwm/Hyprland
synced 2026-05-07 11:58:02 +02:00
Merge ebc8f419a5 into 5f1350f522
This commit is contained in:
commit
82e22ecaff
27 changed files with 1071 additions and 50 deletions
|
|
@ -195,5 +195,952 @@ TEST_CASE(scrollWindowRule) {
|
|||
ASSERT(Tests::windowCount(), 2);
|
||||
|
||||
// not the greatest test, but as long as res and gaps don't change, we good.
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 174,1036");
|
||||
ASSERT_CONTAINS(getFromSocket("/activewindow"), "size: 174,1036");
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocusFalse) {
|
||||
|
||||
/*
|
||||
focuswindow DOES NOT move the scrolling view when follow_focus = false
|
||||
---------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD NOT move scrolling view when follow_focus = false", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("Could not spawn kitty with win class `a`");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("Could not spawn kitty with win class `b`");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
|
||||
// if the view does not move, we expect the x coordinate of the window of class "a" to be negative, as it would be to the left of the viewport
|
||||
const std::string posA = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const int posAx = std::stoi(posA.substr(0, posA.find(',')));
|
||||
if (posAx < 0) {
|
||||
NLog::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::GREEN, Colors::RESET, posAx);
|
||||
} else {
|
||||
FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::RED, Colors::RESET, posAx);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// revert the changes made to config
|
||||
NLog::log("{}Restoring config state", Colors::YELLOW);
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocustrue) {
|
||||
|
||||
/*
|
||||
focuswindow DOES move the view when follow_focus = true
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD move scrolling view when follow_focus = true", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("Could not spawn kitty with win class `a`");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("Could not spawn kitty with win class `b`");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
|
||||
// If the view does not move, we expect the x coordinate of the window of class "a" to be negative, as it would be to the left of the viewport.
|
||||
// If it is not, the view moved, which is what we expect to happen.
|
||||
const std::string posA = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const int posAx = std::stoi(posA.substr(0, posA.find(',')));
|
||||
if (posAx < 0) {
|
||||
FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be >= 0, got {}.", Colors::RED, Colors::RESET, posAx);
|
||||
} else {
|
||||
NLog::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be >= 0, got {}.", Colors::GREEN, Colors::RESET, posAx);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourFocusFallback) {
|
||||
|
||||
/*
|
||||
Focus fallback from killing a floating window onto a tiled window must NOT move scrolling view, regardless of follow_focus
|
||||
--------------------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: focus fallback from floating window to a tiled window should not move scrolling view", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
if (!Tests::spawnKitty("c")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with class `c`", Colors::RED);
|
||||
}
|
||||
|
||||
// make it (window of class:c) float - the view now mush have shifted to fit window class:b
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.float({action = 'enable', window = 'class:c'})"));
|
||||
|
||||
// establish focus history
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})"));
|
||||
|
||||
// kill the floating window
|
||||
// Expect the focus to fall back to the left tiled window
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:c'})"));
|
||||
Tests::waitUntilWindowsN(2);
|
||||
|
||||
// The focus now must have fallen back to tiled window of class "a".
|
||||
|
||||
// If the view did not move, we expect currently focused window's (class:a) to have "at: " x coordinat value <0 (must be left of the viewport)
|
||||
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog ::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourFocusFallbackWithGroups) {
|
||||
|
||||
// same idea as testScrollingViewBehaviourFocusFallback, but with window of class "a" being grouped.
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: focus fallback from floating window to a grouped tiled should not move scrolling view", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test
|
||||
|
||||
// to correctly set up windows for the test
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
// only one tiled window will be grouped for the test
|
||||
OK(getFromSocket("/eval hl.config({group = {auto_group = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
// make it a grouped. There need not be any other windows in the group for this test
|
||||
OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
if (!Tests::spawnKitty("c")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with class `c`", Colors::RED);
|
||||
}
|
||||
|
||||
// make it float - the view now mush have shifted to fit window class:b
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.float({action = 'enable', window = 'class:c'})"));
|
||||
|
||||
// establish focus history
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})"));
|
||||
|
||||
// kill the floating window
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:c'})"));
|
||||
Tests::waitUntilWindowsN(2);
|
||||
|
||||
// The focus now must have fallen back to tiled window of class "a".
|
||||
|
||||
// If the view did not move, we expect currently focused window's (class:a) to have "at: " x coordinat value <0 (must be left of the viewport)
|
||||
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog ::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourWorkspaceChange) {
|
||||
|
||||
/*
|
||||
When you change to a scrolling workspace, the focused window in that workspace must not be pulled into view, regardless of follow_focus
|
||||
---------------------------------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: changing to a scrolling workspace should not move scrolling view", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
|
||||
// switch to workspace 1 for this test
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '1'})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
// does not move view when follow_focus = 0
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
|
||||
// change to workspace 2, then back to workspace 1 again
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '2'})"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '1'})"));
|
||||
|
||||
// If the scrolling view did not move, the x value for `at:` of the currently focused window, class:a, must be <0 (must be left of the viewport)
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourSpecialWorkspaceChange) {
|
||||
|
||||
/*
|
||||
When you change to a special scrolling workspace from a normal workspace, the focused window in that workspace must not be pulled into view, regardless of follow_focus
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: changing to a special scrolling workspace from a normal workspace should not move scrolling view", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
|
||||
// We'll test in this special workspace
|
||||
OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
// does not move view when follow_focus = 0
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
|
||||
// change to workspace 2, then back to special "scroll_S" workspace again
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '2'})"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')"));
|
||||
|
||||
// Reestablish focus since it is finnicky in hyprtester - Harmless and does not move view when follow_focus = 0
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
|
||||
// If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport)
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourSpecialToSpecialWorkspaceChange) {
|
||||
|
||||
/*
|
||||
We also test switching between 2 special workspaces
|
||||
This follows the same idea and dependencies as the test testScrollingViewBehaviourSpecialWorkspaceChange()
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: changing to a special scrolling workspace from another special workspace should not move scrolling view", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
|
||||
// We'll test in this special workspace
|
||||
OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
// does not move view when follow_focus = 0
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
|
||||
// change to special workspace "scroll_F", then back to special "scroll_S" workspace again
|
||||
OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_F')"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')"));
|
||||
|
||||
// Reestablish focus since it is finnicky in hyprtester - Harmless and does not move view when follow_focus = 0
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
|
||||
// If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport)
|
||||
|
||||
const std::string currentWindowPosSPECIAL = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosSPECIALX = currentWindowPosSPECIAL.substr(0, currentWindowPosSPECIAL.find(','));
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosSPECIALX) < 0) {
|
||||
NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosSPECIALX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosSPECIALX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourCloseWindowInGroup) {
|
||||
|
||||
/*
|
||||
When you change close a window inside a group (NOT destroying the group!), it should not cause scrolling view to shift to pull that group into view, regardless of follow_focus
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: closing a window in a group (> 1 window in group) should not move scrolling view", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test
|
||||
|
||||
// this is to avoid unwanted view shifts when setting up the windows
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
// We need 2 windows to be grouped, the third one not.
|
||||
OK(getFromSocket("/eval hl.config({group = {auto_group = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })"));
|
||||
|
||||
if (!Tests::spawnKitty("c")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED);
|
||||
}
|
||||
|
||||
// switch focus to group. This will not move view when follow_focus = 0
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})"));
|
||||
|
||||
// kill window class:b. we expect that this should cause not difference in the position of the group
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:b'})"));
|
||||
Tests::waitUntilWindowsN(2);
|
||||
|
||||
// If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:a, must be <0 (must be left of the viewport)
|
||||
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourMoveWindowIntoGroupFollowFocusFalse) {
|
||||
|
||||
/*
|
||||
when a window is moved inside a group, scrolling view should not move to fit that group when follow_focus = false
|
||||
-----------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: moving a window into a group SHOULD NOT move scrolling view if follow_focus = 0", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
OK(getFromSocket("/eval hl.config({group = {auto_group = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
if (!Tests::spawnKitty("c")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED);
|
||||
}
|
||||
|
||||
// focus class:b
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})"));
|
||||
|
||||
// move it into the group where class:a is
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })"));
|
||||
|
||||
// the focus now should still be on class:b window. If the view did not move, its x coordinate for its `at:` value should be <0
|
||||
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog ::log("{}Passed: {}window of class 'b' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}window of class 'b' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourMoveWindowInGroupFollowFocusTrue) {
|
||||
|
||||
/*
|
||||
when a window is moved inside a group, scrolling view should move to fit that group when follow_focus = true
|
||||
------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: moving a window in a group SHOULD move scrolling view if follow_focus = true", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test
|
||||
OK(getFromSocket("/eval hl.config({group = {auto_group = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
if (!Tests::spawnKitty("c")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED);
|
||||
}
|
||||
|
||||
// focus class:b
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})"));
|
||||
|
||||
// move it into the group where class:a is
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })"));
|
||||
|
||||
// the focus now should still be on class:b window. If the scrolling view did move, its x coordinate for its `at:` value should be >= 0
|
||||
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test fail
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
FAIL_TEST("{}window of class 'b' does not have x coordinates >= 0 for its position: {}", Colors::RED, currentWindowPosX);
|
||||
}
|
||||
// test pass
|
||||
else {
|
||||
NLog ::log("{}Passed: {}window of class 'b' has x coordinates >= 0 for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourNewLayer) {
|
||||
|
||||
/*
|
||||
Starting a program on a different layer shouldn't cause scrolling view to move to fit the window that was focused when this program was started, regardless of follow_focus
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: new program occupying another layer shouldn't move scrolling view", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
// focus class:a - this does not move scrolling view when follow_focus = 0
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
|
||||
NLog::log("{}Spawning kitty layer {}", Colors::YELLOW, "myLayer");
|
||||
if (!Tests::spawnLayerKitty("myLayer")) {
|
||||
FAIL_TEST("{}Error: {} layer did not spawn", Colors::RED, "myLayer");
|
||||
}
|
||||
|
||||
// If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0
|
||||
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all layers
|
||||
NLog::log("{}Killing all layers", Colors::YELLOW);
|
||||
Tests::killAllLayers();
|
||||
ASSERT(Tests::layerCount(), 0);
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourMaximise) {
|
||||
|
||||
/*
|
||||
maximising and then unmaximising a window shouldn't move scrolling view, regardless of follow_focus
|
||||
---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: maximising and then unmaximising a window shouldn't move scrolling view", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
// focus class:a - this does not move scrolling view when follow_focus = 0
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
|
||||
// fullscreen class:a window
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'maximized', action = 'set', window = 'class:a'})"));
|
||||
|
||||
// unfullscreen class:a window
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'maximized', action = 'unset', window = 'class:a'})"));
|
||||
|
||||
// If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0
|
||||
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourFullscreen) {
|
||||
|
||||
/*
|
||||
This is almost the same as the testScrollingViewBehaviourMaximise() test, just with fullscreen 1 (fullscreen) instead of fullscreen 0 (maximise)
|
||||
|
||||
fullscreening and then unfullscreening a window shouldn't move scrolling view, regardless of follow_focus
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: fullscreening and then unfullscreening a window shouldn't move scrolling view", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
// focus class:a - this does not move scrolling view when follow_focus = 0
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})"));
|
||||
|
||||
// maximise class:a window
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'fullscreen', action = 'set', window = 'class:a'})"));
|
||||
|
||||
// unmaximise class:a window
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'fullscreen', action = 'unset', window = 'class:a'})"));
|
||||
|
||||
// If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusFalse) {
|
||||
|
||||
/*
|
||||
dispatching movefocus when follow_focus = false should not cause scrolling view to move
|
||||
---------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: movefocus does not cause scrolling view to move if follow_focus = false", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
// we expect that after dispatching this, scrolling view must not have moved
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})"));
|
||||
|
||||
// If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be < 0.
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusTrue) {
|
||||
|
||||
/*
|
||||
dispatching movefocus when follow_focus = true should cause scrolling view to move
|
||||
----------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: movefocus does cause scrolling view to move if follow_focus = true", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
// we expect that after dispatching this, scrolling view must have moved since follow_focus = true
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})"));
|
||||
|
||||
// If the scrolling view moved, class:a window's x coordinate for its `at:` value should be >= 0
|
||||
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test fail
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have x coordinates >= 0 for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test pass
|
||||
else {
|
||||
NLog ::log("{}Passed: {}window of class 'a' has x coordinates >= 0 for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusFalse) {
|
||||
|
||||
/*
|
||||
When movefocus is dispatched within groups to move focus from one group member to another, scrolling view must not move if follow_focus = false
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: movefocus within groups does not cause scrolling view to move if follow_focus = false", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test
|
||||
|
||||
// necessary to make sure movefocus first cycles through tabs in a group
|
||||
OK(getFromSocket("/eval hl.config({ binds = {movefocus_cycles_groupfirst = true}})"));
|
||||
OK(getFromSocket("/eval hl.config({group = {auto_group = false}})"));
|
||||
OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
if (!Tests::spawnKitty("c")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED);
|
||||
}
|
||||
|
||||
// focus class:b. This does not cause scrolling view to move when follow_focus = false
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})"));
|
||||
|
||||
// move it into the group where class:a is. This does not cause scrolling view to move when follow_focus = false
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })"));
|
||||
|
||||
// we move from one window of a group to another (from class:b to class:a) via movefocus
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})"));
|
||||
|
||||
// the focus now should still be on class:a window. If the scrolling view did not move, its x coordinate for its `at:` value should be < 0
|
||||
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test pass
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test fail
|
||||
else {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusTrue) {
|
||||
|
||||
/*
|
||||
When movefocus is dispatched within groups to move focus from one group member to another, scrolling view must move if follow_focus = true
|
||||
------------------------------------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
NLog::log("{}Testing scrolling view behaviour: movefocus within groups does causes scrolling view to move if follow_focus = true", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
// ensure variables are correctly set for the test
|
||||
|
||||
// necessary to make sure movefocus first cycles through tabs in a group
|
||||
OK(getFromSocket("/eval hl.config({ binds = {movefocus_cycles_groupfirst = true}})"));
|
||||
OK(getFromSocket("/eval hl.config({group = {auto_group = false}})"));
|
||||
|
||||
if (!Tests::spawnKitty("a")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')"));
|
||||
|
||||
if (!Tests::spawnKitty("b")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED);
|
||||
}
|
||||
|
||||
if (!Tests::spawnKitty("c")) {
|
||||
FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED);
|
||||
}
|
||||
|
||||
// focus class:b. This does not cause scrolling view to move when follow_focus = false
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})"));
|
||||
|
||||
// move it into the group where class:a is. This does not cause scrolling view to move when follow_focus = false
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })"));
|
||||
|
||||
// we move from one window of a group to another (from class:b to class:a) via movefocus
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})"));
|
||||
|
||||
// the focus now should still be on class:a window. If the scrolling view moved, its x coordinate for its `at:` value should be >= 0
|
||||
|
||||
const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at");
|
||||
const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(','));
|
||||
// test fail
|
||||
if (std::stoi(currentWindowPosX) < 0) {
|
||||
FAIL_TEST("{}Failed: {}window of class 'a' does not have x coordinates >= 0 for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
// test pass
|
||||
else {
|
||||
NLog ::log("{}Passed: {}window of class 'a' has x coordinates >= 0 for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX);
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
// kill all windows
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
ASSERT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2259,7 +2259,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie
|
|||
Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
|
||||
PWINDOW->updateDecorationValues();
|
||||
g_layoutManager->recalculateMonitor(PMONITOR);
|
||||
g_layoutManager->recalculateMonitor(PMONITOR, Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN);
|
||||
|
||||
// make all windows and layers on the same workspace under the fullscreen window
|
||||
for (auto const& w : m_windows) {
|
||||
|
|
|
|||
|
|
@ -60,10 +60,10 @@ static void switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool forceFSCycle = fals
|
|||
g_pInputManager->unconstrainMouse();
|
||||
|
||||
if (PLASTWINDOW && PLASTWINDOW->m_workspace == PWINDOWTOCHANGETO->m_workspace && PLASTWINDOW->isFullscreen())
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_KEYBIND, nullptr, forceFSCycle);
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_SWITCH_TO_WINDOW_HARD, nullptr, forceFSCycle);
|
||||
else {
|
||||
updateRelativeCursorCoords();
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_KEYBIND, nullptr, forceFSCycle);
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_SWITCH_TO_WINDOW_SOFT, nullptr, forceFSCycle);
|
||||
PWINDOWTOCHANGETO->warpCursor();
|
||||
|
||||
if (*PNOWARPS == 0 || *PFOLLOWMOUSE < 2) {
|
||||
|
|
@ -430,7 +430,7 @@ ActionResult Actions::focus(PHLWINDOW window) {
|
|||
Desktop::focusState()->monitor()->m_activeSpecialWorkspace != window->m_workspace) // NOLINTNEXTLINE
|
||||
Actions::changeWorkspace(PWORKSPACE);
|
||||
|
||||
Desktop::focusState()->fullWindowFocus(window, Desktop::FOCUS_REASON_KEYBIND, nullptr, false);
|
||||
Desktop::focusState()->fullWindowFocus(window, Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW, nullptr, false);
|
||||
window->warpCursor();
|
||||
|
||||
return {};
|
||||
|
|
@ -1269,7 +1269,7 @@ static void moveWindowIntoGroupHelper(PHLWINDOW pWindow, PHLWINDOW pWindowInDire
|
|||
pWindowInDirection->m_group->add(pWindow);
|
||||
pWindowInDirection->m_group->setCurrent(pWindow);
|
||||
pWindow->updateWindowDecos();
|
||||
Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND);
|
||||
Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_DISPATCH_MOVEWINDOWINTOGROUP);
|
||||
pWindow->warpCursor();
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveintogroup", .data = std::format("{:x}", rc<uintptr_t>(pWindow.get()))});
|
||||
|
|
|
|||
|
|
@ -305,5 +305,6 @@ void CFocusState::resetWindowFocus() {
|
|||
}
|
||||
|
||||
bool Desktop::isHardInputFocusReason(eFocusReason r) {
|
||||
return r == FOCUS_REASON_NEW_WINDOW || r == FOCUS_REASON_KEYBIND || r == FOCUS_REASON_GHOSTS || r == FOCUS_REASON_CLICK || r == FOCUS_REASON_DESKTOP_STATE_CHANGE;
|
||||
return r == FOCUS_REASON_NEW_WINDOW || r == FOCUS_REASON_KEYBIND || r == FOCUS_REASON_GHOSTS || r == FOCUS_REASON_CLICK || r == FOCUS_REASON_DESKTOP_STATE_CHANGE ||
|
||||
r == FOCUS_REASON_UNMAP_WINDOW_TILING || r == FOCUS_REASON_SWITCH_TO_WINDOW_HARD;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,19 @@ namespace Desktop {
|
|||
FOCUS_REASON_UNKNOWN = 0,
|
||||
FOCUS_REASON_FFM,
|
||||
FOCUS_REASON_KEYBIND,
|
||||
FOCUS_REASON_DISPATCH_FOCUSWINDOW,
|
||||
FOCUS_REASON_DISPATCH_MOVEWINDOWINTOGROUP,
|
||||
FOCUS_REASON_CLICK,
|
||||
FOCUS_REASON_OTHER,
|
||||
FOCUS_REASON_DESKTOP_STATE_CHANGE,
|
||||
FOCUS_REASON_SWITCH_TO_WINDOW_SOFT,
|
||||
FOCUS_REASON_SWITCH_TO_WINDOW_HARD,
|
||||
FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE,
|
||||
FOCUS_REASON_WORKSPACE_CHANGE,
|
||||
FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE,
|
||||
FOCUS_REASON_UNMAP_WINDOW_TILING,
|
||||
FOCUS_REASON_UNMAP_WINDOW_FLOATING,
|
||||
FOCUS_REASON_UNMAP_GROUPED_WINDOW,
|
||||
FOCUS_REASON_NEW_WINDOW,
|
||||
FOCUS_REASON_GHOSTS,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ void CGroup::add(PHLWINDOW w) {
|
|||
m_target->recalc();
|
||||
}
|
||||
|
||||
void CGroup::remove(PHLWINDOW w, Math::eDirection dir) {
|
||||
void CGroup::remove(PHLWINDOW w, Math::eDirection dir, eRemoveFromGroupReason reason) {
|
||||
std::optional<size_t> idx;
|
||||
for (size_t i = 0; i < m_windows.size(); ++i) {
|
||||
if (m_windows.at(i) == w) {
|
||||
|
|
@ -171,6 +171,10 @@ void CGroup::remove(PHLWINDOW w, Math::eDirection dir) {
|
|||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need to assign a window to a new space if we intend to unmap it
|
||||
if (reason == REMOVE_FROM_GROUP_REASON_UNMAP_WINDOW)
|
||||
return;
|
||||
w->m_target->assignToSpace(m_target->space(), focalPoint);
|
||||
}
|
||||
}
|
||||
|
|
@ -215,7 +219,7 @@ void CGroup::setCurrent(size_t idx) {
|
|||
}
|
||||
|
||||
if (WASFOCUS)
|
||||
Desktop::focusState()->rawWindowFocus(current(), FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
Desktop::focusState()->rawWindowFocus(current(), FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE);
|
||||
}
|
||||
|
||||
void CGroup::setCurrent(PHLWINDOW w) {
|
||||
|
|
|
|||
|
|
@ -15,10 +15,15 @@ namespace Desktop::View {
|
|||
static SP<CGroup> create(std::vector<PHLWINDOWREF>&& windows);
|
||||
~CGroup();
|
||||
|
||||
enum eRemoveFromGroupReason : uint8_t {
|
||||
REMOVE_FROM_GROUP_REASON_UNKNOWN, // when the remove from group reason is unknown or not important to preserve
|
||||
REMOVE_FROM_GROUP_REASON_UNMAP_WINDOW,
|
||||
};
|
||||
|
||||
bool has(PHLWINDOW w) const;
|
||||
|
||||
void add(PHLWINDOW w);
|
||||
void remove(PHLWINDOW w, Math::eDirection dir = Math::DIRECTION_DEFAULT);
|
||||
void remove(PHLWINDOW w, Math::eDirection dir = Math::DIRECTION_DEFAULT, eRemoveFromGroupReason reason = REMOVE_FROM_GROUP_REASON_UNKNOWN);
|
||||
void moveCurrent(bool next);
|
||||
void setCurrent(size_t idx);
|
||||
void setCurrent(PHLWINDOW w);
|
||||
|
|
|
|||
|
|
@ -2361,7 +2361,7 @@ void CWindow::unmapWindow() {
|
|||
PWORKSPACE->m_hasFullscreenWindow = false;
|
||||
|
||||
if (m_group)
|
||||
m_group->remove(m_self.lock());
|
||||
m_group->remove(m_self.lock(), Math::DIRECTION_DEFAULT, CGroup::REMOVE_FROM_GROUP_REASON_UNMAP_WINDOW);
|
||||
|
||||
g_layoutManager->removeTarget(m_target);
|
||||
|
||||
|
|
@ -2390,9 +2390,9 @@ void CWindow::unmapWindow() {
|
|||
|
||||
if (candidate != Desktop::focusState()->window() && candidate) {
|
||||
if (candidate == nextInGroup)
|
||||
Desktop::focusState()->rawWindowFocus(candidate, FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
Desktop::focusState()->rawWindowFocus(candidate, FOCUS_REASON_UNMAP_GROUPED_WINDOW);
|
||||
else
|
||||
Desktop::focusState()->fullWindowFocus(candidate, FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
Desktop::focusState()->fullWindowFocus(candidate, m_self->m_isFloating ? FOCUS_REASON_UNMAP_WINDOW_FLOATING : FOCUS_REASON_UNMAP_WINDOW_TILING);
|
||||
|
||||
if ((*PEXITRETAINSFS || candidate == nextInGroup) && CURRENTWINDOWFSSTATE)
|
||||
g_pCompositor->setWindowFullscreenInternal(candidate, CURRENTFSMODE);
|
||||
|
|
|
|||
|
|
@ -1409,13 +1409,13 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
|
|||
pWindow = pWorkspace->getFocusCandidate();
|
||||
}
|
||||
|
||||
Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND);
|
||||
Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_WORKSPACE_CHANGE);
|
||||
}
|
||||
|
||||
if (!noMouseMove)
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
|
||||
g_layoutManager->recalculateMonitor(m_self.lock());
|
||||
g_layoutManager->recalculateMonitor(m_self.lock(), Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE);
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"workspace", pWorkspace->m_name});
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"workspacev2", std::format("{},{}", pWorkspace->m_id, pWorkspace->m_name)});
|
||||
|
|
@ -1478,11 +1478,11 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
|
|||
if (POLDSPECIAL)
|
||||
POLDSPECIAL->m_events.activeChanged.emit();
|
||||
|
||||
g_layoutManager->recalculateMonitor(m_self.lock());
|
||||
g_layoutManager->recalculateMonitor(m_self.lock(), Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE);
|
||||
|
||||
if (!(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) {
|
||||
if (const auto PLAST = m_activeWorkspace->getLastFocusedWindow(); PLAST)
|
||||
Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND);
|
||||
Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE);
|
||||
else
|
||||
g_pInputManager->refocus();
|
||||
}
|
||||
|
|
@ -1508,7 +1508,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
|
|||
|
||||
if (PMONITOR && PMONITOR->m_activeSpecialWorkspace == pWorkspace) {
|
||||
PMONITOR->m_activeSpecialWorkspace.reset();
|
||||
g_layoutManager->recalculateMonitor(PMONITOR);
|
||||
g_layoutManager->recalculateMonitor(PMONITOR, Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE);
|
||||
g_pHyprRenderer->damageMonitor(PMONITOR);
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMONITOR->m_name});
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + PMONITOR->m_name});
|
||||
|
|
@ -1573,11 +1573,11 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
|
|||
}
|
||||
}
|
||||
|
||||
g_layoutManager->recalculateMonitor(m_self.lock());
|
||||
g_layoutManager->recalculateMonitor(m_self.lock(), Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE);
|
||||
|
||||
if (!(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) {
|
||||
if (const auto PLAST = pWorkspace->getLastFocusedWindow(); PLAST)
|
||||
Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND);
|
||||
Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE);
|
||||
else
|
||||
g_pInputManager->refocus();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -332,18 +332,19 @@ void CLayoutManager::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP<I
|
|||
sourceSize = {sourceX.end - sourceX.start, sourceY.end - sourceY.start};
|
||||
}
|
||||
|
||||
void CLayoutManager::recalculateMonitor(PHLMONITOR m) {
|
||||
void CLayoutManager::recalculateMonitor(PHLMONITOR m, eRecalculateMonitorReason reason) {
|
||||
if (m->m_activeSpecialWorkspace)
|
||||
m->m_activeSpecialWorkspace->m_space->recalculate();
|
||||
m->m_activeSpecialWorkspace->m_space->recalculate(recalcMonitorReasonToRecalcReason(reason));
|
||||
|
||||
if (m->m_activeWorkspace)
|
||||
m->m_activeWorkspace->m_space->recalculate();
|
||||
m->m_activeWorkspace->m_space->recalculate(recalcMonitorReasonToRecalcReason(reason));
|
||||
}
|
||||
|
||||
void CLayoutManager::invalidateMonitorGeometries(PHLMONITOR m) {
|
||||
for (const auto& ws : g_pCompositor->getWorkspaces()) {
|
||||
if (ws && ws->m_monitor == m) {
|
||||
ws->m_space->recheckWorkArea();
|
||||
ws->m_space->recalculate();
|
||||
ws->m_space->recalculate(RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,13 @@ namespace Layout {
|
|||
CLayoutManager();
|
||||
~CLayoutManager() = default;
|
||||
|
||||
enum eRecalculateMonitorReason : uint8_t {
|
||||
RECALCULATE_MONITOR_REASON_UNKNOWN, // when the recalculate monitor reason is unknown or not important to preserve
|
||||
RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE,
|
||||
RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE,
|
||||
RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN,
|
||||
};
|
||||
|
||||
void newTarget(SP<ITarget> target, SP<CSpace> space);
|
||||
void removeTarget(SP<ITarget> target);
|
||||
|
||||
|
|
@ -76,7 +83,7 @@ namespace Layout {
|
|||
void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP<ITarget> target, eMouseBindMode mode, int corner, const Vector2D& beginSize);
|
||||
|
||||
void invalidateMonitorGeometries(PHLMONITOR);
|
||||
void recalculateMonitor(PHLMONITOR);
|
||||
void recalculateMonitor(PHLMONITOR, eRecalculateMonitorReason reason = RECALCULATE_MONITOR_REASON_UNKNOWN);
|
||||
|
||||
const UP<Supplementary::CDragStateController>& dragController();
|
||||
|
||||
|
|
|
|||
|
|
@ -101,9 +101,9 @@ size_t CAlgorithm::floatingTargets() const {
|
|||
return m_floatingTargets.size();
|
||||
}
|
||||
|
||||
void CAlgorithm::recalculate() {
|
||||
m_tiled->recalculate();
|
||||
m_floating->recalculate();
|
||||
void CAlgorithm::recalculate(eRecalculateReason reason) {
|
||||
m_tiled->recalculate(reason);
|
||||
m_floating->recalculate(reason);
|
||||
|
||||
const auto PWORKSPACE = m_space->workspace();
|
||||
if (!PWORKSPACE)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "../../helpers/memory/Memory.hpp"
|
||||
|
||||
#include "../LayoutManager.hpp"
|
||||
#include "../space/Space.hpp"
|
||||
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
|
|
@ -34,7 +35,7 @@ namespace Layout {
|
|||
Config::ErrorResult layoutMsg(const std::string_view& sv);
|
||||
std::optional<Vector2D> predictSizeForNewTiledTarget();
|
||||
|
||||
void recalculate();
|
||||
void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN);
|
||||
void recenter(SP<ITarget> t);
|
||||
|
||||
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
using namespace Layout;
|
||||
|
||||
void IFloatingAlgorithm::recalculate() {
|
||||
void IFloatingAlgorithm::recalculate(eRecalculateReason reason) {
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace Layout {
|
|||
|
||||
virtual void recenter(SP<ITarget> t);
|
||||
|
||||
virtual void recalculate();
|
||||
virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN);
|
||||
|
||||
protected:
|
||||
IFloatingAlgorithm() = default;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "../../helpers/memory/Memory.hpp"
|
||||
|
||||
#include "../LayoutManager.hpp"
|
||||
#include "../space/Space.hpp"
|
||||
|
||||
#include <expected>
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ namespace Layout {
|
|||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE) = 0;
|
||||
|
||||
// recalculate layout
|
||||
virtual void recalculate() = 0;
|
||||
virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN) = 0;
|
||||
|
||||
// swap targets
|
||||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b) = 0;
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ void CDwindleAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
|
|||
nodeB->pTarget = a;
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::recalculate() {
|
||||
void CDwindleAlgorithm::recalculate(eRecalculateReason reason) {
|
||||
calculateWorkspace();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace Layout::Tiled {
|
|||
virtual void removeTarget(SP<ITarget> target);
|
||||
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void recalculate();
|
||||
virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN);
|
||||
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
|
|
|
|||
|
|
@ -424,7 +424,7 @@ void CMasterAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir
|
|||
}
|
||||
}
|
||||
|
||||
void CMasterAlgorithm::recalculate() {
|
||||
void CMasterAlgorithm::recalculate(eRecalculateReason reason) {
|
||||
calculateWorkspace();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ namespace Layout::Tiled {
|
|||
virtual void removeTarget(SP<ITarget> target);
|
||||
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void recalculate();
|
||||
virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN);
|
||||
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ void CMonocleAlgorithm::resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRe
|
|||
// monocle layout doesn't support manual resizing, all windows are fullscreen
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::recalculate() {
|
||||
void CMonocleAlgorithm::recalculate(eRecalculateReason reason) {
|
||||
if (m_targetDatas.empty())
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace Layout::Tiled {
|
|||
virtual void removeTarget(SP<ITarget> target);
|
||||
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void recalculate();
|
||||
virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN);
|
||||
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
|
|
|
|||
|
|
@ -535,7 +535,16 @@ CScrollingAlgorithm::CScrollingAlgorithm() {
|
|||
if (!TARGET || TARGET->floating())
|
||||
return;
|
||||
|
||||
focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_KB : INPUT_MODE_SOFT));
|
||||
// if follow_focus != 0, focuswindow always moves scrolling view
|
||||
// if follow_focus != 0, change in a group's current window state always moves scrolling view
|
||||
// if follow_focus != 0, moving a window into group via the corresponding dispatches `moveintogroup`, `movewindoworgroup` always moves scrolling view
|
||||
// if follow_focus != 0, moving focus via dispatches that cause switching to a specific window via calling switchToWindow(), such as movefocus, cyclenext, focuscurrentor(last/urgent); always moves scrolling view
|
||||
if (*PFOLLOW_FOCUS &&
|
||||
(reason == Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW || reason == Desktop::FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE ||
|
||||
reason == Desktop::FOCUS_REASON_DISPATCH_MOVEWINDOWINTOGROUP || reason == Desktop::FOCUS_REASON_SWITCH_TO_WINDOW_SOFT))
|
||||
focusOnInput(TARGET, INPUT_MODE_HARD);
|
||||
else
|
||||
focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_HARD : INPUT_MODE_SOFT));
|
||||
});
|
||||
|
||||
// Initialize default widths and direction
|
||||
|
|
@ -576,7 +585,7 @@ void CScrollingAlgorithm::focusOnInput(SP<ITarget> target, eInputMode input) {
|
|||
}
|
||||
|
||||
// if we moved via non-kb, and it's fully visible, ignore
|
||||
if (m_scrollingData->visible(TARGETDATA->column.lock(), true) && input != INPUT_MODE_KB)
|
||||
if (m_scrollingData->visible(TARGETDATA->column.lock(), true) && input != INPUT_MODE_HARD)
|
||||
return;
|
||||
|
||||
static const auto PFITMETHOD = CConfigValue<Config::INTEGER>("scrolling:focus_fit_method");
|
||||
|
|
@ -796,7 +805,7 @@ void CScrollingAlgorithm::resizeTarget(const Vector2D& delta, SP<ITarget> target
|
|||
m_scrollingData->recalculate(true);
|
||||
}
|
||||
|
||||
void CScrollingAlgorithm::recalculate() {
|
||||
void CScrollingAlgorithm::recalculate(eRecalculateReason reason) {
|
||||
// guard against recalculation during transitional monitor states
|
||||
// (e.g. monitor reconnecting after suspend where workspace/monitor may not be ready)
|
||||
if (!m_parent || !m_parent->space() || !m_parent->space()->workspace() || !m_parent->space()->workspace()->m_monitor)
|
||||
|
|
@ -807,8 +816,14 @@ void CScrollingAlgorithm::recalculate() {
|
|||
|
||||
const auto TARGETDATA = dataFor(TARGET);
|
||||
|
||||
if (TARGETDATA && !m_scrollingData->visible(TARGETDATA->column.lock(), true))
|
||||
focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_KB);
|
||||
if (TARGETDATA && !m_scrollingData->visible(TARGETDATA->column.lock(), true)) {
|
||||
|
||||
/* guard against unwanted scrolling viewport moves - If recalculate() was called, it is assumed that either the INPUT_MODE will be HARD (i.e. it is meant to move the scrolling viewport) or
|
||||
it is not meant to move the scrolling viewport.
|
||||
(e.g. changing workspace to a scrolling layout workspace fits the focused window in that workspace into view) */
|
||||
if (Layout::isHardRecalculateReason(reason))
|
||||
focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
m_scrollingData->recalculate();
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ namespace Layout::Tiled {
|
|||
virtual void removeTarget(SP<ITarget> target);
|
||||
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void recalculate();
|
||||
virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN);
|
||||
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ namespace Layout::Tiled {
|
|||
enum eInputMode : uint8_t {
|
||||
INPUT_MODE_SOFT = 0,
|
||||
INPUT_MODE_CLICK,
|
||||
INPUT_MODE_KB
|
||||
INPUT_MODE_HARD
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -146,11 +146,11 @@ SP<CAlgorithm> CSpace::algorithm() const {
|
|||
return m_algorithm;
|
||||
}
|
||||
|
||||
void CSpace::recalculate() {
|
||||
void CSpace::recalculate(eRecalculateReason reason) {
|
||||
recheckWorkArea();
|
||||
|
||||
if (m_algorithm)
|
||||
m_algorithm->recalculate();
|
||||
m_algorithm->recalculate(reason);
|
||||
}
|
||||
|
||||
void CSpace::setFullscreen(SP<ITarget> t, eFullscreenMode mode) {
|
||||
|
|
@ -159,7 +159,7 @@ void CSpace::setFullscreen(SP<ITarget> t, eFullscreenMode mode) {
|
|||
if (mode == FSMODE_NONE && m_algorithm && t->floating())
|
||||
m_algorithm->recenter(t);
|
||||
|
||||
recalculate();
|
||||
recalculate(RECALCULATE_REASON_TOGGLE_FULLSCREEN);
|
||||
}
|
||||
|
||||
Config::ErrorResult CSpace::layoutMsg(const std::string_view& sv) {
|
||||
|
|
@ -202,6 +202,21 @@ SP<ITarget> CSpace::getNextCandidate(SP<ITarget> old) {
|
|||
return !m_algorithm ? nullptr : m_algorithm->getNextCandidate(old);
|
||||
}
|
||||
|
||||
bool Layout::isHardRecalculateReason(eRecalculateReason reason) {
|
||||
return reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_TOGGLE_FULLSCREEN &&
|
||||
reason != RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES && reason != RECALCULATE_REASON_RENDER_MOINTOR;
|
||||
}
|
||||
|
||||
const std::vector<WP<ITarget>>& CSpace::targets() const {
|
||||
return m_targets;
|
||||
}
|
||||
|
||||
eRecalculateReason Layout::recalcMonitorReasonToRecalcReason(CLayoutManager::eRecalculateMonitorReason reason) {
|
||||
// If eRecalculateMonitorReason doesn't have a eRecalculateReason pair, it'll return nullopt
|
||||
switch (reason) {
|
||||
case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE: return RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE;
|
||||
case CLayoutManager::RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE: return RECALCULATE_REASON_WORKSPACE_CHANGE;
|
||||
case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN: return RECALCULATE_REASON_TOGGLE_FULLSCREEN;
|
||||
default: return RECALCULATE_REASON_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,18 @@
|
|||
#include <expected>
|
||||
|
||||
namespace Layout {
|
||||
|
||||
enum eRecalculateReason : uint8_t {
|
||||
RECALCULATE_REASON_UNKNOWN, // when the recalculate reason is unknown or not important to preserve
|
||||
RECALCULATE_REASON_WORKSPACE_CHANGE,
|
||||
RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE,
|
||||
RECALCULATE_REASON_TOGGLE_FULLSCREEN,
|
||||
RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES,
|
||||
RECALCULATE_REASON_RENDER_MOINTOR,
|
||||
};
|
||||
|
||||
eRecalculateReason recalcMonitorReasonToRecalcReason(CLayoutManager::eRecalculateMonitorReason reason);
|
||||
|
||||
class ITarget;
|
||||
class CAlgorithm;
|
||||
|
||||
|
|
@ -34,7 +46,7 @@ namespace Layout {
|
|||
|
||||
void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
void recalculate();
|
||||
void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN);
|
||||
|
||||
void toggleTargetFloating(SP<ITarget> t);
|
||||
|
||||
|
|
@ -68,4 +80,6 @@ namespace Layout {
|
|||
// for recalc
|
||||
CHyprSignalListener m_geomUpdateCallback;
|
||||
};
|
||||
|
||||
bool isHardRecalculateReason(eRecalculateReason reason);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2002,7 +2002,7 @@ void IHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
|
|||
if (pMonitor->m_scheduledRecalc) {
|
||||
pMonitor->m_scheduledRecalc = false;
|
||||
if (pMonitor->m_activeWorkspace) // might be missing (mirror)
|
||||
pMonitor->m_activeWorkspace->m_space->recalculate();
|
||||
pMonitor->m_activeWorkspace->m_space->recalculate(Layout::RECALCULATE_REASON_RENDER_MOINTOR);
|
||||
}
|
||||
|
||||
if (!pMonitor->m_output->needsFrame && pMonitor->m_forceFullFrames == 0)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue