hluk / copyq-commands

Useful commands for CopyQ clipboard manager.
328 stars 72 forks source link

Cycle items and paste with single hotkey in Wayland #97

Closed XP1 closed 3 months ago

XP1 commented 3 months ago

I can't get the original version from https://github.com/hluk/CopyQ/issues/1948 working on Wayland. The ItemSelection().current() and selectedTab() are both empty, so I had to write my own version that saves the selected row to the settings.

In my version, I prefer a single one-handed shortcut, similar to how Alt + Tab and Ctrl + Tab works, so I use Ctrl + 1 for forwards and Ctrl + Shift + 1 for backwards.

Add the commands including the "wayland-support.ini" in CopyQ. Next, in KDE, add the shortcuts in System Settings > Shortcuts > Add Command... > Add.

cycle-previous-and-next-items.ini:

[Commands]
1\Command="
    copyq:
    // ==CopyQ==
    // @name Cycle previous and next items
    // @version 1.00
    // @description Cycles through previous items with Ctrl + Shift + 1 and next items with Ctrl + 1 and then pastes the selected item when the shortcut modifier key is released.
    // @namespace https://github.com/XP1/
    // @copyright 2024
    // @author XP1
    // @homepage https://github.com/XP1/
    // @license GNU General Public License, Version 3.0; https://www.gnu.org/licenses/gpl-3.0.html
    // ==/CopyQ==

    /*
     * Copyright 2024 XP1
     *
     * 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 <http://www.gnu.org/licenses/>.
     */

    /*jslint devel, long, unordered */
    /*global str, settings, focused, queryKeyboardModifiers, select, selectItems, sleep, show, hide, paste */
    (function () {
        \"use strict\";

        const selectedRowOption = \"CycleItems/selectedRow\";
        const isWaitingKeyOption = \"CycleItems/isWaitingKey\";

        function getSelectedRow() {
            return parseInt(str(settings(selectedRowOption)), 10);
        }

        function isWaitingKey() {
            return (str(settings(isWaitingKeyOption)) === \"true\");
        }

        function previousRow() {
            let row = getSelectedRow();
            row = (row > 1 ? (row - 1) : 0);
            settings(selectedRowOption, row);
            selectItems(row);
            return row;
        }

        function nextRow() {
            let row = getSelectedRow();
            row = (row > 0 ? (row + 1) : 1);
            settings(selectedRowOption, row);
            selectItems(row);
            return row;
        }

        function waitKey() {
            if (isWaitingKey()) {
                return;
            }

            settings(isWaitingKeyOption, true);

            // Wait for shortcut modifiers to be released.
            while (queryKeyboardModifiers().length > 0) {
                sleep(20);
            }

            const row = getSelectedRow();
            if (row >= 0) {
                select(row);
                hide();
                paste();
            }

            settings(isWaitingKeyOption, false);
        }

        function initialize(direction) {
            if (focused()) {
                if (direction === \"previous\") {
                    previousRow();
                } else {
                    nextRow();
                }
            } else {
                settings(selectedRowOption, -1);
                selectItems(0);
                show();
            }

            waitKey();
        }

        initialize(\"previous\");
    }());"
1\GlobalShortcut=ctrl+shift+1
1\Icon=\xf1b8
1\IsGlobalShortcut=true
1\Name=Cycle previous items
1\Shortcut=ctrl+shift+1
2\Command="
    copyq:
    // ==CopyQ==
    // @name Cycle previous and next items
    // @version 1.00
    // @description Cycles through previous items with Ctrl + Shift + 1 and next items with Ctrl + 1 and then pastes the selected item when the shortcut modifier key is released.
    // @namespace https://github.com/XP1/
    // @copyright 2024
    // @author XP1
    // @homepage https://github.com/XP1/
    // @license GNU General Public License, Version 3.0; https://www.gnu.org/licenses/gpl-3.0.html
    // ==/CopyQ==

    /*
     * Copyright 2024 XP1
     *
     * 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 <http://www.gnu.org/licenses/>.
     */

    /*jslint devel, long, unordered */
    /*global str, settings, focused, queryKeyboardModifiers, select, selectItems, sleep, show, hide, paste */
    (function () {
        \"use strict\";

        const selectedRowOption = \"CycleItems/selectedRow\";
        const isWaitingKeyOption = \"CycleItems/isWaitingKey\";

        function getSelectedRow() {
            return parseInt(str(settings(selectedRowOption)), 10);
        }

        function isWaitingKey() {
            return (str(settings(isWaitingKeyOption)) === \"true\");
        }

        function previousRow() {
            let row = getSelectedRow();
            row = (row > 1 ? (row - 1) : 0);
            settings(selectedRowOption, row);
            selectItems(row);
            return row;
        }

        function nextRow() {
            let row = getSelectedRow();
            row = (row > 0 ? (row + 1) : 1);
            settings(selectedRowOption, row);
            selectItems(row);
            return row;
        }

        function waitKey() {
            if (isWaitingKey()) {
                return;
            }

            settings(isWaitingKeyOption, true);

            // Wait for shortcut modifiers to be released.
            while (queryKeyboardModifiers().length > 0) {
                sleep(20);
            }

            const row = getSelectedRow();
            if (row >= 0) {
                select(row);
                hide();
                paste();
            }

            settings(isWaitingKeyOption, false);
        }

        function initialize(direction) {
            if (focused()) {
                if (direction === \"previous\") {
                    previousRow();
                } else {
                    nextRow();
                }
            } else {
                settings(selectedRowOption, -1);
                selectItems(0);
                show();
            }

            waitKey();
        }

        initialize(\"next\");
    }());"
2\GlobalShortcut=ctrl+1
2\Icon=\xf1b8
2\IsGlobalShortcut=true
2\Name=Cycle next items
2\Shortcut=ctrl+1
size=2
hluk commented 3 months ago

I can't get the original version from hluk/CopyQ#1948 working on Wayland. The ItemSelection().current() and selectedTab() are both empty, so I had to write my own version that saves the selected row to the settings.

Hmm, true, getting current and selected items works only if the command was triggered from the app and won't work when it is run from console or by a system shortcut binding. It might be better to remove the restriction and allow any command to get the list of selected items when first requested (if not yet available).

BTW, thanks for sharing the workaround!

hluk commented 3 months ago

In the next CopyQ release, the selected items will be available in all commands. Fix: https://github.com/hluk/CopyQ/pull/new/fix-native-notifications