Unverified Commit 08109b9b authored by AUTOMATIC1111's avatar AUTOMATIC1111 Committed by GitHub
Browse files

Merge pull request #10902 from daswer123/dev

Improvement for zoom builtin extension
parents 58779b28 5b682be5
Loading
Loading
Loading
Loading
+152 −19
Original line number Diff line number Diff line
@@ -14,13 +14,73 @@ function getActiveTab(elements, all = false) {
    }
}

// Wait until opts loaded
async function waitForOpts() {
    return new Promise(resolve => {
        const checkInterval = setInterval(() => {
            if (window.opts && Object.keys(window.opts).length !== 0) {
                clearInterval(checkInterval);
                resolve(window.opts);
            }
        }, 100);
    });
}

// Check is hotkey valid
function isSingleLetter(value) {
    return (
        typeof value === "string" && value.length === 1 && /[a-z]/i.test(value)
    );
}

// Create hotkeyConfig from opts
function createHotkeyConfig(defaultHotkeysConfig, hotkeysConfigOpts) {
    const result = {};
    const usedKeys = new Set();

    for (const key in defaultHotkeysConfig) {
        if (typeof hotkeysConfigOpts[key] === "boolean") {
            result[key] = hotkeysConfigOpts[key];
            continue;
        }
        if (
            hotkeysConfigOpts[key] &&
            isSingleLetter(hotkeysConfigOpts[key]) &&
            !usedKeys.has(hotkeysConfigOpts[key].toUpperCase())
        ) {
            // If the property passed the test and has not yet been used, add 'Key' before it and save it
            result[key] = "Key" + hotkeysConfigOpts[key].toUpperCase();
            usedKeys.add(hotkeysConfigOpts[key].toUpperCase());
        } else {
            // If the property does not pass the test or has already been used, we keep the default value
            console.error(
                `Hotkey: ${hotkeysConfigOpts[key]} for ${key} is repeated and conflicts with another hotkey or is not 1 letter. The default hotkey is used: ${defaultHotkeysConfig[key][3]}`
            );
            result[key] = defaultHotkeysConfig[key];
        }
    }

    return result;
}

// Main
onUiLoaded(async() => {
    const hotkeysConfig = {
        resetZoom: "KeyR",
        fitToScreen: "KeyS",
        moveKey: "KeyF",
        overlap: "KeyO"
    const hotkeysConfigOpts = await waitForOpts();

    // Default config
    const defaultHotkeysConfig = {
        canvas_hotkey_reset: "KeyR",
        canvas_hotkey_fullscreen: "KeyS",
        canvas_hotkey_move: "KeyF",
        canvas_hotkey_overlap: "KeyO",
        canvas_show_tooltip: true,
        canvas_swap_controls: false
    };
    // swap the actions for ctr + wheel and shift + wheel
    const hotkeysConfig = createHotkeyConfig(
        defaultHotkeysConfig,
        hotkeysConfigOpts
    );

    let isMoving = false;
    let mouseX, mouseY;
@@ -48,6 +108,68 @@ onUiLoaded(async() => {
        let [zoomLevel, panX, panY] = [1, 0, 0];
        let fullScreenMode = false;

        // Create tooltip
        function createTooltip() {
            const toolTipElemnt =
                targetElement.querySelector(".image-container");
            const tooltip = document.createElement("div");
            tooltip.className = "tooltip";

            // Creating an item of information
            const info = document.createElement("i");
            info.className = "tooltip-info";
            info.textContent = "";

            // Create a container for the contents of the tooltip
            const tooltipContent = document.createElement("div");
            tooltipContent.className = "tooltip-content";

            // Add info about hotkets
            const zoomKey = hotkeysConfig.canvas_swap_controls ? "Ctrl" : "Shift";
            const adjustKey = hotkeysConfig.canvas_swap_controls ? "Shift" : "Ctrl";

            const hotkeys = [
                {key: `${zoomKey} + wheel`, action: "Zoom canvas"},
                {key: `${adjustKey} + wheel`, action: "Adjust brush size"},
                {
                    key: hotkeysConfig.canvas_hotkey_reset.charAt(
                        hotkeysConfig.canvas_hotkey_reset.length - 1
                    ),
                    action: "Reset zoom"
                },
                {
                    key: hotkeysConfig.canvas_hotkey_fullscreen.charAt(
                        hotkeysConfig.canvas_hotkey_fullscreen.length - 1
                    ),
                    action: "Fullscreen mode"
                },
                {
                    key: hotkeysConfig.canvas_hotkey_move.charAt(
                        hotkeysConfig.canvas_hotkey_move.length - 1
                    ),
                    action: "Move canvas"
                }
            ];
            hotkeys.forEach(function(hotkey) {
                const p = document.createElement("p");
                p.innerHTML =
                    "<b>" + hotkey.key + "</b>" + " - " + hotkey.action;
                tooltipContent.appendChild(p);
            });

            // Add information and content elements to the tooltip element
            tooltip.appendChild(info);
            tooltip.appendChild(tooltipContent);

            // Add a hint element to the target element
            toolTipElemnt.appendChild(tooltip);
        }

        //Show tool tip if setting enable
        if (hotkeysConfig.canvas_show_tooltip) {
            createTooltip();
        }

        // In the course of research, it was found that the tag img is very harmful when zooming and creates white canvases. This hack allows you to almost never think about this problem, it has no effect on webui.
        function fixCanvas() {
            const activeTab = getActiveTab(elements).textContent.trim();
@@ -159,7 +281,10 @@ onUiLoaded(async() => {

        // Change the zoom level based on user interaction
        function changeZoomLevel(operation, e) {
            if (e.shiftKey) {
            if (
                (!hotkeysConfig.canvas_swap_controls && e.shiftKey) ||
                (hotkeysConfig.canvas_swap_controls && e.ctrlKey)
            ) {
                e.preventDefault();

                let zoomPosX, zoomPosY;
@@ -262,7 +387,8 @@ onUiLoaded(async() => {
            targetElement.style.transform = `translate(${0}px, ${0}px) scale(${1})`;

            // Get scrollbar width to right-align the image
            const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
            const scrollbarWidth =
                window.innerWidth - document.documentElement.clientWidth;

            // Get element and screen dimensions
            const elementWidth = targetElement.offsetWidth;
@@ -312,10 +438,9 @@ onUiLoaded(async() => {
        // Handle keydown events
        function handleKeyDown(event) {
            const hotkeyActions = {
                [hotkeysConfig.resetZoom]: resetZoom,
                [hotkeysConfig.overlap]: toggleOverlap,
                [hotkeysConfig.fitToScreen]: fitToScreen
                // [hotkeysConfig.moveKey] : moveCanvas,
                [hotkeysConfig.canvas_hotkey_reset]: resetZoom,
                [hotkeysConfig.canvas_hotkey_overlap]: toggleOverlap,
                [hotkeysConfig.canvas_hotkey_fullscreen]: fitToScreen
            };

            const action = hotkeyActions[event.code];
@@ -369,7 +494,11 @@ onUiLoaded(async() => {
            changeZoomLevel(operation, e);

            // Handle brush size adjustment with ctrl key pressed
            if (e.ctrlKey || e.metaKey) {
            if (
                (hotkeysConfig.canvas_swap_controls && e.shiftKey) ||
                (!hotkeysConfig.canvas_swap_controls &&
                    (e.ctrlKey || e.metaKey))
            ) {
                e.preventDefault();

                // Increase or decrease brush size based on scroll direction
@@ -377,20 +506,19 @@ onUiLoaded(async() => {
            }
        });

        /**
         * Handle the move event for pan functionality. Updates the panX and panY variables and applies the new transform to the target element.
         * @param {MouseEvent} e - The mouse event.
         */
        // Handle the move event for pan functionality. Updates the panX and panY variables and applies the new transform to the target element.
        function handleMoveKeyDown(e) {
            if (e.code === hotkeysConfig.moveKey) {
                if (!e.ctrlKey && !e.metaKey) {
            if (e.code === hotkeysConfig.canvas_hotkey_move) {
                if (!e.ctrlKey && !e.metaKey && isKeyDownHandlerAttached) {
                    e.preventDefault();
                    document.activeElement.blur();
                    isMoving = true;
                }
            }
        }

        function handleMoveKeyUp(e) {
            if (e.code === hotkeysConfig.moveKey) {
            if (e.code === hotkeysConfig.canvas_hotkey_move) {
                isMoving = false;
            }
        }
@@ -422,6 +550,11 @@ onUiLoaded(async() => {
            }
        }

        // Prevents sticking to the mouse
        window.onblur = function() {
            isMoving = false;
        };

        gradioApp().addEventListener("mousemove", handleMoveByKey);
    }

+10 −0
Original line number Diff line number Diff line
from modules import shared

shared.options_templates.update(shared.options_section(('canvas_hotkey', "Canvas Hotkeys"), {
    "canvas_hotkey_move": shared.OptionInfo("F", "Moving the canvas"),
    "canvas_hotkey_fullscreen": shared.OptionInfo("S", "Fullscreen Mode, maximizes the picture so that it fits into the screen and stretches it to its full width "),
    "canvas_hotkey_reset": shared.OptionInfo("R", "Reset zoom and canvas positon"),
    "canvas_hotkey_overlap": shared.OptionInfo("O", "Toggle overlap ( Technical button, neededs for testing )"),
    "canvas_show_tooltip": shared.OptionInfo(True, "Enable tooltip on the canvas"),
    "canvas_swap_controls": shared.OptionInfo(False, "Swap hotkey combinations for Zoom and Adjust brush resize"),
}))
+63 −0
Original line number Diff line number Diff line
.tooltip-info {
  position: absolute;
  top: 10px;
  left: 10px;
  cursor: help;
  background-color: rgba(0, 0, 0, 0.3);
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column; 

  z-index: 100;
}

.tooltip-info::after {
  content: '';
  display: block;
  width: 2px;
  height: 7px;
  background-color: white;
  margin-top: 2px; 
}

.tooltip-info::before {
  content: '';
  display: block;
  width: 2px;
  height: 2px;
  background-color: white;
}

.tooltip-content {
  display: none;
  background-color: #f9f9f9; 
  color: #333; 
  border: 1px solid #ddd;
  padding: 15px; 
  position: absolute;
  top: 40px;
  left: 10px;
  width: 250px;
  font-size: 16px;
  opacity: 0;
  border-radius: 8px;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); 

  z-index: 100;
}

.tooltip:hover .tooltip-content {
  display: block;
  animation: fadeIn 0.5s; 
  opacity: 1;
}

@keyframes fadeIn {
  from {opacity: 0;}
  to {opacity: 1;}
}