Czy mogę włączyć przewijanie za pomocą przeciągania środkowego przycisku w OS X?

16

Mam mysz z trzema przyciskami, ale bez kółka.

Czy w OS X jest jakiś sposób (być może z oprogramowaniem dodatkowym), który pozwoliłby mi używać trzeciego przycisku do przewijania, przytrzymując go i przesuwając mysz?

kdt
źródło

Odpowiedzi:

10

Smart Scroll robi to, czego szukasz, dzięki funkcji „Grab Scroll”. Przypisz go do przycisku 3 (środkowy), a przeciąganie na obie osie będzie działać w aplikacjach takich jak przeglądarki (Chrome), terminal, Adobe Photoshop i Finder - żadna aplikacja, z której korzystałem, nie działała z nim (przy użyciu 4.0 bety w górę i w górę). Ma darmowy okres próbny.

wprowadź opis zdjęcia tutaj

Ishan
źródło
3

Zrobiłem to z Hammerspoon z następującym skryptem konfiguracyjnym zainspirowanym tym wątkiem: https://github.com/tekezo/Karabiner/issues/814#issuecomment-337643019

Kroki:

  • Zainstaluj Hammerspoon
  • Kliknij ikonę menu i wybierz Open Config
  • Wklej następujący luaskrypt do konfiguracji:

    -- HANDLE SCROLLING WITH MOUSE BUTTON PRESSED
    local scrollMouseButton = 2
    local deferred = false
    
    overrideOtherMouseDown = hs.eventtap.new({ hs.eventtap.event.types.otherMouseDown }, function(e)
        -- print("down")
        local pressedMouseButton = e:getProperty(hs.eventtap.event.properties['mouseEventButtonNumber'])
        if scrollMouseButton == pressedMouseButton 
            then 
                deferred = true
                return true
            end
    end)
    
    overrideOtherMouseUp = hs.eventtap.new({ hs.eventtap.event.types.otherMouseUp }, function(e)
        -- print("up")
        local pressedMouseButton = e:getProperty(hs.eventtap.event.properties['mouseEventButtonNumber'])
        if scrollMouseButton == pressedMouseButton 
            then 
                if (deferred) then
                    overrideOtherMouseDown:stop()
                    overrideOtherMouseUp:stop()
                    hs.eventtap.otherClick(e:location(), pressedMouseButton)
                    overrideOtherMouseDown:start()
                    overrideOtherMouseUp:start()
                    return true
                end
                return false
            end
            return false
    end)
    
    local oldmousepos = {}
    local scrollmult = -4   -- negative multiplier makes mouse work like traditional scrollwheel
    
    dragOtherToScroll = hs.eventtap.new({ hs.eventtap.event.types.otherMouseDragged }, function(e)
        local pressedMouseButton = e:getProperty(hs.eventtap.event.properties['mouseEventButtonNumber'])
        -- print ("pressed mouse " .. pressedMouseButton)
        if scrollMouseButton == pressedMouseButton 
            then 
                -- print("scroll");
                deferred = false
                oldmousepos = hs.mouse.getAbsolutePosition()    
                local dx = e:getProperty(hs.eventtap.event.properties['mouseEventDeltaX'])
                local dy = e:getProperty(hs.eventtap.event.properties['mouseEventDeltaY'])
                local scroll = hs.eventtap.event.newScrollEvent({-dx * scrollmult, dy * scrollmult},{},'pixel')
                -- put the mouse back
                hs.mouse.setAbsolutePosition(oldmousepos)
                return true, {scroll}
            else 
                return false, {}
            end 
    end)
    
    overrideOtherMouseDown:start()
    overrideOtherMouseUp:start()
    dragOtherToScroll:start()
    
Alex Burdusel
źródło
właśnie go wypróbowałem i działa pięknie.
im_chc
ponieważ wolę zrobić przewijanie Y na odwrót, nieco zmieniłem kod lua: zmień „dy” w wierszu „local scroll = hs.eventtap.event.newScrollEvent ({- dx * scrollmult, dy * scrollmult}, {}, „pixel”) ”na minus (więc byłoby„ -dy * scrollmult ”)
im_chc
2

Smooze robi to między innymi. (Jestem programistą)

Tym, co odróżnia go od innych sugestii, jest możliwość korzystania z niego w każdej aplikacji Mac, przy jednoczesnym identyfikowaniu na przykład linków. (w przypadku użycia środkowego przycisku przeciągnij, aby złapać i rzucić, ale nadal chcesz, aby środkowe kliknięcie działało jak środkowy przycisk)

Dzięki Smooze bardziej przypomina chwytanie i przeciąganie niż chwytanie. Wydanie wpływa na pęd i animację zwoju, podobnie jak w przypadku zwoju iPhone'a.

wprowadź opis zdjęcia tutaj

Segev
źródło
2

Jest bardzo ładna aplikacja open source o nazwie Karabiner, która to zrobi i wiele więcej (mapowanie klawiatury i myszy itp.). Zobacz to pytanie dla niektórych przykładów. Również dla niektórych producentów dostarczają niestandardowe oprogramowanie sterujące, które może umożliwić ulepszoną / zmodyfikowaną funkcjonalność (np. Logitech Control Center).

Jak wspomniano w komentarzach poniżej, podczas gdy nowa wersja „Karabiner Elements” została wydana dla MacOS Sierra (10.12) i późniejszych, zapewnia ona tylko do tej pory mapowanie oparte na klawiaturze - więc obecnie nie można z tym zrobić remapowania myszy.

Jednak Hammerspoon to kolejne bezpłatne narzędzie typu open source, które może być używane, między innymi, do zmiany przypisania klawiszy myszy (i / lub klawiatury) do różnych funkcji. Musisz zainstalować narzędzie i dostarczyć odpowiednią konfigurację - zobacz przykłady mapowania myszy tutaj .

Aby sprawdzić, jakie typy zdarzeń i mouseEventButtonNumbers są generowane przez urządzenie, możesz uruchomić to (po prostu skopiuj / wklej 4 linie do konsoli) w konsoli Hammerspoon (użyj, reload configaby to zatrzymać):

hs.eventtap.new({"all"},function(e)
print(e,"mouseEventButtonNumber:",
e:getProperty(hs.eventtap.event.properties['mouseEventButtonNumber']))
end):start()

Uwaga: Jeśli zainstalowałeś narzędzia Logitech Control Center (LCC) - pobiera zdarzenia bezpośrednio z urządzeń Logitech za pomocą zainstalowanego modułu jądra, aby Hammerspoon ich nie widział. Musisz odinstalować LCC, jeśli chcesz ponownie przypisać przyciski myszy za pomocą Hammerspoon.

Pierz
źródło
1
Niestety Karabiner nie działa już w nowoczesnym OSX. Jest teraz „Karabiner Elements”, ale ma on połowę funkcjonalności oryginału i jest to jedna z rzeczy, których nie może zrobić.
Nathan Hornby
1
Tak, obecnie jest ograniczony, dlatego zaktualizowałem swoją odpowiedź, aby dodać kolejne rozwiązanie.
Pierz
Hammerspoon to rozwiązanie, na które wylądowałem wczoraj, więc dobra sugestia! :) Z jakiegoś powodu nie udało mi się powiązać go z jednym z przycisków myszy, ale mapowanie go do ctrl + cmd wydawało się działać dobrze.
Nathan Hornby
1
Dodałem kolejną edycję, ponieważ miałem ten problem, gdy miałem zainstalowany LCC, ale odinstalowałem go, naprawiłem go (raz pracowałem, które przyciski wygenerowały który mouseEventButtonNumber - na mojej marmurowej myszy lewy mini przycisk to 3, a prawy to 4) .
Pierz
Podejrzewałem, że to może być problem! Dzięki za potwierdzenie, wyjaśnię to, kiedy będę miał szansę.
Nathan Hornby
1

Zależy to od oprogramowania - na przykład Firefox go obsługuje, a Google Chrome nie.

Niestety, nie ma oprogramowania, które umożliwi takie funkcje w całym systemie OS X, niestety.


źródło
Być może nie był kompatybilny z Chrome w 2011 roku, ale z pewnością w 2014 roku, po bez wątpienia kilku poprawkach, „Grab Scroll” Smart Scroll działa płynnie z Chrome i Operą, mogę potwierdzić. Wydaje mi się, że jest on również dla całego systemu operacyjnego, ponieważ działa w Finderze, Adobe Photoshopie, a nawet Terminalu. Myślę więc, że twoje dane są nieaktualne! :)
1

Użyłem narzędzia Better Touch Tool, aby przypisać Ctrl + środkowe kliknięcie do PgUp, a Option + środkowe kliknięcie do PgDown. To bezpłatne, doskonałe oprogramowanie i działa dobrze.

ezrock
źródło
1

+1 za Hammerspoon i skrypt, normalna mysz / trackball doprowadza mnie do szaleństwa na komputerze Mac.

Napisałem jeden do przewijania, gdy środkowy przycisk myszy jest wciśnięty - im bardziej poruszasz myszą, tym szybciej się przewija.

Kliknięcie nadal działa jak normalne kliknięcie z 5-pikselową strefą martwą, więc nie musisz utrzymywać myszy idealnie nieruchomej między naciśnięciem i zwolnieniem koła.

------------------------------------------------------------------------------------------
-- AUTOSCROLL WITH MOUSE WHEEL BUTTON
-- timginter @ GitHub
------------------------------------------------------------------------------------------

-- id of mouse wheel button
local mouseScrollButtonId = 2

-- scroll speed and direction config
local scrollSpeedMultiplier = 0.1
local scrollSpeedSquareAcceleration = true
local reverseVerticalScrollDirection = false
local mouseScrollTimerDelay = 0.01

-- circle config
local mouseScrollCircleRad = 10
local mouseScrollCircleDeadZone = 5

------------------------------------------------------------------------------------------

local mouseScrollCircle = nil
local mouseScrollTimer = nil
local mouseScrollStartPos = 0
local mouseScrollDragPosX = nil
local mouseScrollDragPosY = nil

overrideScrollMouseDown = hs.eventtap.new({ hs.eventtap.event.types.otherMouseDown }, function(e)
    -- uncomment line below to see the ID of pressed button
    --print(e:getProperty(hs.eventtap.event.properties['mouseEventButtonNumber']))

    if e:getProperty(hs.eventtap.event.properties['mouseEventButtonNumber']) == mouseScrollButtonId then
        -- remove circle if exists
        if mouseScrollCircle then
            mouseScrollCircle:delete()
            mouseScrollCircle = nil
        end

        -- stop timer if running
        if mouseScrollTimer then
            mouseScrollTimer:stop()
            mouseScrollTimer = nil
        end

        -- save mouse coordinates
        mouseScrollStartPos = hs.mouse.getAbsolutePosition()
        mouseScrollDragPosX = mouseScrollStartPos.x
        mouseScrollDragPosY = mouseScrollStartPos.y

        -- start scroll timer
        mouseScrollTimer = hs.timer.doAfter(mouseScrollTimerDelay, mouseScrollTimerFunction)

        -- don't send scroll button down event
        return true
    end
end)

overrideScrollMouseUp = hs.eventtap.new({ hs.eventtap.event.types.otherMouseUp }, function(e)
    if e:getProperty(hs.eventtap.event.properties['mouseEventButtonNumber']) == mouseScrollButtonId then
        -- send original button up event if released within 'mouseScrollCircleDeadZone' pixels of original position and scroll circle doesn't exist
        mouseScrollPos = hs.mouse.getAbsolutePosition()
        xDiff = math.abs(mouseScrollPos.x - mouseScrollStartPos.x)
        yDiff = math.abs(mouseScrollPos.y - mouseScrollStartPos.y)
        if (xDiff < mouseScrollCircleDeadZone and yDiff < mouseScrollCircleDeadZone) and not mouseScrollCircle then
            -- disable scroll mouse override
            overrideScrollMouseDown:stop()
            overrideScrollMouseUp:stop()

            -- send scroll mouse click
            hs.eventtap.otherClick(e:location(), mouseScrollButtonId)

            -- re-enable scroll mouse override
            overrideScrollMouseDown:start()
            overrideScrollMouseUp:start()
        end

        -- remove circle if exists
        if mouseScrollCircle then
            mouseScrollCircle:delete()
            mouseScrollCircle = nil
        end

        -- stop timer if running
        if mouseScrollTimer then
            mouseScrollTimer:stop()
            mouseScrollTimer = nil
        end

        -- don't send scroll button up event
        return true
    end
end)

overrideScrollMouseDrag = hs.eventtap.new({ hs.eventtap.event.types.otherMouseDragged }, function(e)
    -- sanity check
    if mouseScrollDragPosX == nil or mouseScrollDragPosY == nil then
        return true
    end

    -- update mouse coordinates
    mouseScrollDragPosX = mouseScrollDragPosX + e:getProperty(hs.eventtap.event.properties['mouseEventDeltaX'])
    mouseScrollDragPosY = mouseScrollDragPosY + e:getProperty(hs.eventtap.event.properties['mouseEventDeltaY'])

    -- don't send scroll button drag event
    return true
end)

function mouseScrollTimerFunction()
    -- sanity check
    if mouseScrollDragPosX ~= nil and mouseScrollDragPosY ~= nil then
        -- get cursor position difference from original click
        xDiff = math.abs(mouseScrollDragPosX - mouseScrollStartPos.x)
        yDiff = math.abs(mouseScrollDragPosY - mouseScrollStartPos.y)

        -- draw circle if not yet drawn and cursor moved more than 'mouseScrollCircleDeadZone' pixels
        if mouseScrollCircle == nil and (xDiff > mouseScrollCircleDeadZone or yDiff > mouseScrollCircleDeadZone) then
            mouseScrollCircle = hs.drawing.circle(hs.geometry.rect(mouseScrollStartPos.x - mouseScrollCircleRad, mouseScrollStartPos.y - mouseScrollCircleRad, mouseScrollCircleRad * 2, mouseScrollCircleRad * 2))
            mouseScrollCircle:setStrokeColor({["red"]=0.3, ["green"]=0.3, ["blue"]=0.3, ["alpha"]=1})
            mouseScrollCircle:setFill(false)
            mouseScrollCircle:setStrokeWidth(1)
            mouseScrollCircle:show()
        end

        -- send scroll event if cursor moved more than circle's radius
        if xDiff > mouseScrollCircleRad or yDiff > mouseScrollCircleRad then
            -- get real xDiff and yDiff
            deltaX = mouseScrollDragPosX - mouseScrollStartPos.x
            deltaY = mouseScrollDragPosY - mouseScrollStartPos.y

            -- use 'scrollSpeedMultiplier'
            deltaX = deltaX * scrollSpeedMultiplier
            deltaY = deltaY * scrollSpeedMultiplier

            -- square for better scroll acceleration
            if scrollSpeedSquareAcceleration then
                -- mod to keep negative values
                deltaXDirMod = 1
                deltaYDirMod = 1

                if deltaX < 0 then
                    deltaXDirMod = -1
                end
                if deltaY < 0 then
                    deltaYDirMod = -1
                end

                deltaX = deltaX * deltaX * deltaXDirMod
                deltaY = deltaY * deltaY * deltaYDirMod
            end

            -- math.floor - scroll event accepts only integers
            deltaX = math.floor(deltaX)
            deltaY = math.floor(deltaY)

            -- reverse Y scroll if 'reverseVerticalScrollDirection' set to true
            if reverseVerticalScrollDirection then
                deltaY = deltaY * -1
            end

            -- send scroll event
            hs.eventtap.event.newScrollEvent({-deltaX, deltaY}, {}, 'pixel'):post()
        end
    end

    -- restart timer
    mouseScrollTimer = hs.timer.doAfter(mouseScrollTimerDelay, mouseScrollTimerFunction)
end

-- start override functions
overrideScrollMouseDown:start()
overrideScrollMouseUp:start()
overrideScrollMouseDrag:start()

------------------------------------------------------------------------------------------
-- END OF AUTOSCROLL WITH MOUSE WHEEL BUTTON
------------------------------------------------------------------------------------------
TIM
źródło
Funkcja zabójcy! Dziękuję bardzo, dokładnie to, czego szukałem. Jeden błąd jednak deltaX = deltaY * -1powinien być deltaY = deltaY * -1i skomentowałem, deltaX = deltaX * -1ponieważ nie chciałem, aby oś X była odwrócona.
Tyler
Dzięki za zauważenie literówki. Trochę go przepisałem i zmieniłem opcję odwracania tylko pionowego przewijania
TIM