wip(cef): refactor touchpad input handling
- commit
- 05dede819ad3c0cb05c4899f3ce6b947e271ddc3
- author
- brice · · 2026-06-08 20:21 UTC
- parent
- 1b29d2c
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
15 files · +119 -163
M docs/config/index.md +2 -2
| @@ -195,12 +195,12 @@ border = "#363636" | |||
| 195 | 195 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled, otherwise CEF default | | |
| 196 | 196 | | `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | Hard cap for adaptive CEF OSR FPS; 0 falls back to the built-in default cap selected by Dumber/CEF | | |
| 197 | 197 | | `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | > 0 | Mouse wheel scroll sensitivity multiplier | | |
| 198 | - | | `engine.cef.input.scroll_touchpad_multiplier` | float | `0.35` | > 0 | Touchpad/smooth scroll sensitivity multiplier | | |
| 198 | + | | `engine.cef.input.scroll_precise_multiplier` | float | `2.5` | > 0 | Precise/surface scroll sensitivity multiplier | | |
| 199 | 199 | | `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | > 0 | Horizontal scroll sensitivity multiplier | | |
| 200 | 200 | | `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | > 0 | Vertical scroll sensitivity multiplier | | |
| 201 | 201 | | `engine.cef.input.scroll_max_delta` | int32 | `0` | >= 0 | Maximum absolute scroll delta after scaling; 0 disables clamping | | |
| 202 | 202 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | - | Enable two-finger touchpad swipe back/forward navigation | | |
| 203 | - | | `engine.cef.input.touchpad_navigation_min_delta` | float | `80.0` | > 0 | Minimum accumulated horizontal swipe delta required for navigation | | |
| 203 | + | | `engine.cef.input.touchpad_navigation_min_delta` | float | `15.0` | > 0 | Minimum accumulated horizontal swipe delta required for navigation | | |
| 204 | 204 | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | Maximum vertical-to-horizontal delta ratio allowed for navigation swipes | | |
| 205 | 205 | | `default_ui_scale` | float | `1.0` | > 0 | GTK widget UI scale (1.0=100%, 2.0=200%) | | |
| 206 | 206 | | `default_webpage_zoom` | float | `1.2` | > 0 | Default page zoom (1.0=100%, 1.2=120%) | | |
| @@ -195,12 +195,12 @@ border = "#363636" | |||||
| 195 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled, otherwise CEF default | | 195 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled, otherwise CEF default | | ||
| 196 | | `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | Hard cap for adaptive CEF OSR FPS; 0 falls back to the built-in default cap selected by Dumber/CEF | | 196 | | `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | Hard cap for adaptive CEF OSR FPS; 0 falls back to the built-in default cap selected by Dumber/CEF | | ||
| 197 | | `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | > 0 | Mouse wheel scroll sensitivity multiplier | | 197 | | `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | > 0 | Mouse wheel scroll sensitivity multiplier | | ||
| 198 | - | | `engine.cef.input.scroll_touchpad_multiplier` | float | `0.35` | > 0 | Touchpad/smooth scroll sensitivity multiplier | | 198 | + | | `engine.cef.input.scroll_precise_multiplier` | float | `2.5` | > 0 | Precise/surface scroll sensitivity multiplier | |
| 199 | | `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | > 0 | Horizontal scroll sensitivity multiplier | | 199 | | `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | > 0 | Horizontal scroll sensitivity multiplier | | ||
| 200 | | `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | > 0 | Vertical scroll sensitivity multiplier | | 200 | | `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | > 0 | Vertical scroll sensitivity multiplier | | ||
| 201 | | `engine.cef.input.scroll_max_delta` | int32 | `0` | >= 0 | Maximum absolute scroll delta after scaling; 0 disables clamping | | 201 | | `engine.cef.input.scroll_max_delta` | int32 | `0` | >= 0 | Maximum absolute scroll delta after scaling; 0 disables clamping | | ||
| 202 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | - | Enable two-finger touchpad swipe back/forward navigation | | 202 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | - | Enable two-finger touchpad swipe back/forward navigation | | ||
| 203 | - | | `engine.cef.input.touchpad_navigation_min_delta` | float | `80.0` | > 0 | Minimum accumulated horizontal swipe delta required for navigation | | 203 | + | | `engine.cef.input.touchpad_navigation_min_delta` | float | `15.0` | > 0 | Minimum accumulated horizontal swipe delta required for navigation | |
| 204 | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | Maximum vertical-to-horizontal delta ratio allowed for navigation swipes | | 204 | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | Maximum vertical-to-horizontal delta ratio allowed for navigation swipes | | ||
| 205 | | `default_ui_scale` | float | `1.0` | > 0 | GTK widget UI scale (1.0=100%, 2.0=200%) | | 205 | | `default_ui_scale` | float | `1.0` | > 0 | GTK widget UI scale (1.0=100%, 2.0=200%) | | ||
| 206 | | `default_webpage_zoom` | float | `1.2` | > 0 | Default page zoom (1.0=100%, 1.2=120%) | | 206 | | `default_webpage_zoom` | float | `1.2` | > 0 | Default page zoom (1.0=100%, 1.2=120%) | | ||
M docs/reference/configuration.md +2 -2
| @@ -65,12 +65,12 @@ | |||
| 65 | 65 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | | |
| 66 | 66 | | `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | | |
| 67 | 67 | | `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | > 0 | | |
| 68 | - | | `engine.cef.input.scroll_touchpad_multiplier` | float | `0.35` | > 0 | | |
| 68 | + | | `engine.cef.input.scroll_precise_multiplier` | float | `2.5` | > 0 | | |
| 69 | 69 | | `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | > 0 | | |
| 70 | 70 | | `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | > 0 | | |
| 71 | 71 | | `engine.cef.input.scroll_max_delta` | int32 | `0` | >= 0 | | |
| 72 | 72 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | | | |
| 73 | - | | `engine.cef.input.touchpad_navigation_min_delta` | float | `80.0` | > 0 | | |
| 73 | + | | `engine.cef.input.touchpad_navigation_min_delta` | float | `15.0` | > 0 | | |
| 74 | 74 | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | | |
| 75 | 75 | | `default_ui_scale` | float | `1.0` | > 0 | | |
| 76 | 76 | | `default_webpage_zoom` | float | `1.2` | > 0 | | |
| @@ -65,12 +65,12 @@ | |||||
| 65 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | | 65 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | | ||
| 66 | | `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | | 66 | | `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | | ||
| 67 | | `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | > 0 | | 67 | | `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | > 0 | | ||
| 68 | - | | `engine.cef.input.scroll_touchpad_multiplier` | float | `0.35` | > 0 | | 68 | + | | `engine.cef.input.scroll_precise_multiplier` | float | `2.5` | > 0 | |
| 69 | | `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | > 0 | | 69 | | `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | > 0 | | ||
| 70 | | `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | > 0 | | 70 | | `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | > 0 | | ||
| 71 | | `engine.cef.input.scroll_max_delta` | int32 | `0` | >= 0 | | 71 | | `engine.cef.input.scroll_max_delta` | int32 | `0` | >= 0 | | ||
| 72 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | | | 72 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | | | ||
| 73 | - | | `engine.cef.input.touchpad_navigation_min_delta` | float | `80.0` | > 0 | | 73 | + | | `engine.cef.input.touchpad_navigation_min_delta` | float | `15.0` | > 0 | |
| 74 | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | | 74 | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | | ||
| 75 | | `default_ui_scale` | float | `1.0` | > 0 | | 75 | | `default_ui_scale` | float | `1.0` | > 0 | | ||
| 76 | | `default_webpage_zoom` | float | `1.2` | > 0 | | 76 | | `default_webpage_zoom` | float | `1.2` | > 0 | | ||
M go.mod +2 -0
| @@ -83,3 +83,5 @@ require ( | |||
| 83 | 83 | ) | |
| 84 | 84 | ||
| 85 | 85 | tool github.com/a-h/templ/cmd/templ | |
| 86 | + | ||
| 87 | + | replace github.com/bnema/purego-cef2gtk => ../purego-cef2gtk | |
| @@ -83,3 +83,5 @@ require ( | |||||
| 83 | ) | 83 | ) | ||
| 84 | 84 | ||||
| 85 | tool github.com/a-h/templ/cmd/templ | 85 | tool github.com/a-h/templ/cmd/templ | ||
| 86 | + | ||||
| 87 | + | replace github.com/bnema/purego-cef2gtk => ../purego-cef2gtk | |||
M internal/bootstrap/engine.go +1 -1
| @@ -86,7 +86,7 @@ func BuildEngine(input EngineInput) (port.Engine, error) { | |||
| 86 | 86 | WindowlessFrameRateMax: cfg.Engine.CEF.CEFWindowlessFrameRateMax(), | |
| 87 | 87 | Input: cef.RuntimeInputConfig{ | |
| 88 | 88 | ScrollWheelMultiplier: cfg.Engine.CEF.Input.ScrollWheelMultiplier, | |
| 89 | - | ScrollTouchpadMultiplier: cfg.Engine.CEF.Input.ScrollTouchpadMultiplier, | |
| 89 | + | ScrollPreciseMultiplier: cfg.Engine.CEF.Input.ScrollPreciseMultiplier, | |
| 90 | 90 | ScrollHorizontalMultiplier: cfg.Engine.CEF.Input.ScrollHorizontalMultiplier, | |
| 91 | 91 | ScrollVerticalMultiplier: cfg.Engine.CEF.Input.ScrollVerticalMultiplier, | |
| 92 | 92 | ScrollMaxDelta: cfg.Engine.CEF.Input.ScrollMaxDelta, | |
| @@ -86,7 +86,7 @@ func BuildEngine(input EngineInput) (port.Engine, error) { | |||||
| 86 | WindowlessFrameRateMax: cfg.Engine.CEF.CEFWindowlessFrameRateMax(), | 86 | WindowlessFrameRateMax: cfg.Engine.CEF.CEFWindowlessFrameRateMax(), | ||
| 87 | Input: cef.RuntimeInputConfig{ | 87 | Input: cef.RuntimeInputConfig{ | ||
| 88 | ScrollWheelMultiplier: cfg.Engine.CEF.Input.ScrollWheelMultiplier, | 88 | ScrollWheelMultiplier: cfg.Engine.CEF.Input.ScrollWheelMultiplier, | ||
| 89 | - | ScrollTouchpadMultiplier: cfg.Engine.CEF.Input.ScrollTouchpadMultiplier, | 89 | + | ScrollPreciseMultiplier: cfg.Engine.CEF.Input.ScrollPreciseMultiplier, |
| 90 | ScrollHorizontalMultiplier: cfg.Engine.CEF.Input.ScrollHorizontalMultiplier, | 90 | ScrollHorizontalMultiplier: cfg.Engine.CEF.Input.ScrollHorizontalMultiplier, | ||
| 91 | ScrollVerticalMultiplier: cfg.Engine.CEF.Input.ScrollVerticalMultiplier, | 91 | ScrollVerticalMultiplier: cfg.Engine.CEF.Input.ScrollVerticalMultiplier, | ||
| 92 | ScrollMaxDelta: cfg.Engine.CEF.Input.ScrollMaxDelta, | 92 | ScrollMaxDelta: cfg.Engine.CEF.Input.ScrollMaxDelta, | ||
M internal/infrastructure/cef/runtime_config.go +1 -1
| @@ -30,7 +30,7 @@ type RuntimeConfig struct { | |||
| 30 | 30 | ||
| 31 | 31 | type RuntimeInputConfig struct { | |
| 32 | 32 | ScrollWheelMultiplier float64 | |
| 33 | - | ScrollTouchpadMultiplier float64 | |
| 33 | + | ScrollPreciseMultiplier float64 | |
| 34 | 34 | ScrollHorizontalMultiplier float64 | |
| 35 | 35 | ScrollVerticalMultiplier float64 | |
| 36 | 36 | ScrollMaxDelta int32 | |
| @@ -30,7 +30,7 @@ type RuntimeConfig struct { | |||||
| 30 | 30 | ||||
| 31 | type RuntimeInputConfig struct { | 31 | type RuntimeInputConfig struct { | ||
| 32 | ScrollWheelMultiplier float64 | 32 | ScrollWheelMultiplier float64 | ||
| 33 | - | ScrollTouchpadMultiplier float64 | 33 | + | ScrollPreciseMultiplier float64 |
| 34 | ScrollHorizontalMultiplier float64 | 34 | ScrollHorizontalMultiplier float64 | ||
| 35 | ScrollVerticalMultiplier float64 | 35 | ScrollVerticalMultiplier float64 | ||
| 36 | ScrollMaxDelta int32 | 36 | ScrollMaxDelta int32 | ||
M internal/infrastructure/cef/webview.go +79 -94
| @@ -225,8 +225,16 @@ type WebView struct { | |||
| 225 | 225 | windowlessFrameRate int32 | |
| 226 | 226 | backgroundColor uint32 | |
| 227 | 227 | inputConfig RuntimeInputConfig | |
| 228 | - | touchpadSwipeDX float64 | |
| 229 | - | touchpadSwipeDY float64 | |
| 228 | + | ||
| 229 | + | // Touchpad input diagnostics are debounced to avoid flooding logs while | |
| 230 | + | // debugging continuous scroll streams. | |
| 231 | + | inputDiagMu sync.Mutex | |
| 232 | + | inputDiagLastLog time.Time | |
| 233 | + | inputDiagEvents int | |
| 234 | + | inputDiagDX float64 | |
| 235 | + | inputDiagDY float64 | |
| 236 | + | inputDiagDeltaXSum int64 | |
| 237 | + | inputDiagDeltaYSum int64 | |
| 230 | 238 | ||
| 231 | 239 | // Audio output factory and active stream. | |
| 232 | 240 | audioOutputFactory port.AudioOutputFactory | |
| @@ -1462,7 +1470,7 @@ func (wv *WebView) bridgeInputOptions() cef2gtk.InputOptions { | |||
| 1462 | 1470 | Scale: 0, | |
| 1463 | 1471 | Scroll: cef2gtk.ScrollOptions{ | |
| 1464 | 1472 | WheelMultiplier: wv.inputConfig.ScrollWheelMultiplier, | |
| 1465 | - | TouchpadMultiplier: wv.inputConfig.ScrollTouchpadMultiplier, | |
| 1473 | + | PreciseMultiplier: wv.inputConfig.ScrollPreciseMultiplier, | |
| 1466 | 1474 | HorizontalMultiplier: wv.inputConfig.ScrollHorizontalMultiplier, | |
| 1467 | 1475 | VerticalMultiplier: wv.inputConfig.ScrollVerticalMultiplier, | |
| 1468 | 1476 | MaxDelta: wv.inputConfig.ScrollMaxDelta, | |
| @@ -1470,13 +1478,16 @@ func (wv *WebView) bridgeInputOptions() cef2gtk.InputOptions { | |||
| 1470 | 1478 | OnMiddleClick: func(_, _ float64) bool { | |
| 1471 | 1479 | return wv.handleMiddleClickFromBridge() | |
| 1472 | 1480 | }, | |
| 1473 | - | OnTouchpadSwipe: func(event cef2gtk.TouchpadSwipeEvent) cef2gtk.TouchpadSwipeDecision { | |
| 1474 | - | if wv.handleTouchpadNavigationSwipe(event) { | |
| 1475 | - | return cef2gtk.TouchpadSwipeConsume | |
| 1476 | - | } | |
| 1477 | - | return cef2gtk.TouchpadSwipePassthrough | |
| 1481 | + | OnScroll: wv.handleScrollInputDiagnostic, | |
| 1482 | + | NavigationSwipe: cef2gtk.NavigationSwipeOptions{ | |
| 1483 | + | Enabled: wv.inputConfig.TouchpadNavigationEnabled, | |
| 1484 | + | MinDelta: wv.inputConfig.TouchpadNavigationMinDelta, | |
| 1485 | + | MaxVerticalRatio: wv.inputConfig.TouchpadNavigationMaxVerticalRatio, | |
| 1478 | 1486 | }, | |
| 1479 | - | SelectionText: wv.selectedTextSnapshot, | |
| 1487 | + | CanNavigateBack: wv.CanGoBack, | |
| 1488 | + | CanNavigateForward: wv.CanGoForward, | |
| 1489 | + | OnNavigateSwipe: wv.handleNavigationSwipeAction, | |
| 1490 | + | SelectionText: wv.selectedTextSnapshot, | |
| 1480 | 1491 | OnClipboardShortcut: func(action, text string) { | |
| 1481 | 1492 | if wv.engine != nil { | |
| 1482 | 1493 | wv.engine.handleExplicitClipboardBridgeText(wv.id, action, text) | |
| @@ -1485,95 +1496,69 @@ func (wv *WebView) bridgeInputOptions() cef2gtk.InputOptions { | |||
| 1485 | 1496 | } | |
| 1486 | 1497 | } | |
| 1487 | 1498 | ||
| 1488 | - | type touchpadNavigationAction int | |
| 1489 | - | ||
| 1490 | - | const ( | |
| 1491 | - | touchpadNavigationNone touchpadNavigationAction = iota | |
| 1492 | - | touchpadNavigationBack | |
| 1493 | - | touchpadNavigationForward | |
| 1494 | - | ) | |
| 1495 | - | ||
| 1496 | - | func (wv *WebView) handleTouchpadNavigationSwipe(event cef2gtk.TouchpadSwipeEvent) bool { | |
| 1497 | - | cfg := wv.inputConfig | |
| 1498 | - | if !cfg.TouchpadNavigationEnabled || event.Fingers != 2 { | |
| 1499 | - | wv.resetTouchpadNavigationSwipe() | |
| 1500 | - | return false | |
| 1499 | + | func (wv *WebView) handleScrollInputDiagnostic(event cef2gtk.ScrollEvent) cef2gtk.ScrollDecision { | |
| 1500 | + | if event.Phase != cef2gtk.ScrollPhaseUpdate || math.Abs(event.DX) == 0 { | |
| 1501 | + | return cef2gtk.ScrollForwardToCEF | |
| 1501 | 1502 | } | |
| 1502 | 1503 | ||
| 1503 | - | switch event.Phase { | |
| 1504 | - | case cef2gtk.TouchpadGesturePhaseBegin: | |
| 1505 | - | wv.resetTouchpadNavigationSwipe() | |
| 1506 | - | return false | |
| 1507 | - | case cef2gtk.TouchpadGesturePhaseUpdate: | |
| 1508 | - | wv.accumulateTouchpadNavigationSwipe(event.DX, event.DY) | |
| 1509 | - | return false | |
| 1510 | - | case cef2gtk.TouchpadGesturePhaseEnd: | |
| 1511 | - | dx, dy := wv.finishTouchpadNavigationSwipe() | |
| 1512 | - | switch chooseTouchpadNavigationAction(cfg, dx, dy, wv.CanGoBack(), wv.CanGoForward()) { | |
| 1513 | - | case touchpadNavigationBack: | |
| 1514 | - | if err := wv.GoBack(wv.ctx); err != nil { | |
| 1515 | - | logging.FromContext(wv.ctx).Warn().Err(err).Msg("touchpad back navigation failed") | |
| 1516 | - | return false | |
| 1517 | - | } | |
| 1518 | - | return true | |
| 1519 | - | case touchpadNavigationForward: | |
| 1520 | - | if err := wv.GoForward(wv.ctx); err != nil { | |
| 1521 | - | logging.FromContext(wv.ctx).Warn().Err(err).Msg("touchpad forward navigation failed") | |
| 1522 | - | return false | |
| 1523 | - | } | |
| 1524 | - | return true | |
| 1525 | - | default: | |
| 1526 | - | return false | |
| 1527 | - | } | |
| 1528 | - | case cef2gtk.TouchpadGesturePhaseCancel: | |
| 1529 | - | wv.resetTouchpadNavigationSwipe() | |
| 1530 | - | return false | |
| 1531 | - | default: | |
| 1532 | - | return false | |
| 1533 | - | } | |
| 1534 | - | } | |
| 1535 | - | ||
| 1536 | - | func (wv *WebView) resetTouchpadNavigationSwipe() { | |
| 1537 | - | wv.mu.Lock() | |
| 1538 | - | wv.touchpadSwipeDX = 0 | |
| 1539 | - | wv.touchpadSwipeDY = 0 | |
| 1540 | - | wv.mu.Unlock() | |
| 1541 | - | } | |
| 1542 | - | ||
| 1543 | - | func (wv *WebView) accumulateTouchpadNavigationSwipe(dx, dy float64) { | |
| 1544 | - | wv.mu.Lock() | |
| 1545 | - | wv.touchpadSwipeDX += dx | |
| 1546 | - | wv.touchpadSwipeDY += dy | |
| 1547 | - | wv.mu.Unlock() | |
| 1548 | - | } | |
| 1549 | - | ||
| 1550 | - | func (wv *WebView) finishTouchpadNavigationSwipe() (float64, float64) { | |
| 1551 | - | wv.mu.Lock() | |
| 1552 | - | defer wv.mu.Unlock() | |
| 1553 | - | dx, dy := wv.touchpadSwipeDX, wv.touchpadSwipeDY | |
| 1554 | - | wv.touchpadSwipeDX = 0 | |
| 1555 | - | wv.touchpadSwipeDY = 0 | |
| 1556 | - | return dx, dy | |
| 1557 | - | } | |
| 1504 | + | wv.inputDiagMu.Lock() | |
| 1505 | + | wv.inputDiagEvents++ | |
| 1506 | + | wv.inputDiagDX += event.DX | |
| 1507 | + | wv.inputDiagDY += event.DY | |
| 1508 | + | wv.inputDiagDeltaXSum += int64(event.DeltaX) | |
| 1509 | + | wv.inputDiagDeltaYSum += int64(event.DeltaY) | |
| 1510 | + | now := time.Now() | |
| 1511 | + | if !wv.inputDiagLastLog.IsZero() && now.Sub(wv.inputDiagLastLog) < time.Second { | |
| 1512 | + | wv.inputDiagMu.Unlock() | |
| 1513 | + | return cef2gtk.ScrollForwardToCEF | |
| 1514 | + | } | |
| 1515 | + | count := wv.inputDiagEvents | |
| 1516 | + | dx, dy := wv.inputDiagDX, wv.inputDiagDY | |
| 1517 | + | deltaX, deltaY := wv.inputDiagDeltaXSum, wv.inputDiagDeltaYSum | |
| 1518 | + | wv.inputDiagEvents = 0 | |
| 1519 | + | wv.inputDiagDX = 0 | |
| 1520 | + | wv.inputDiagDY = 0 | |
| 1521 | + | wv.inputDiagDeltaXSum = 0 | |
| 1522 | + | wv.inputDiagDeltaYSum = 0 | |
| 1523 | + | wv.inputDiagLastLog = now | |
| 1524 | + | wv.inputDiagMu.Unlock() | |
| 1558 | 1525 | ||
| 1559 | - | func chooseTouchpadNavigationAction(cfg RuntimeInputConfig, dx, dy float64, canGoBack, canGoForward bool) touchpadNavigationAction { | |
| 1560 | - | if !cfg.TouchpadNavigationEnabled { | |
| 1561 | - | return touchpadNavigationNone | |
| 1562 | - | } | |
| 1563 | - | absDX, absDY := math.Abs(dx), math.Abs(dy) | |
| 1564 | - | if absDX < cfg.TouchpadNavigationMinDelta { | |
| 1565 | - | return touchpadNavigationNone | |
| 1566 | - | } | |
| 1567 | - | if absDY > absDX*cfg.TouchpadNavigationMaxVerticalRatio { | |
| 1568 | - | return touchpadNavigationNone | |
| 1569 | - | } | |
| 1570 | - | if dx > 0 && canGoBack { | |
| 1571 | - | return touchpadNavigationBack | |
| 1572 | - | } | |
| 1573 | - | if dx < 0 && canGoForward { | |
| 1574 | - | return touchpadNavigationForward | |
| 1526 | + | logging.FromContext(wv.ctx).Debug(). | |
| 1527 | + | Uint64("webview_id", uint64(wv.id)). | |
| 1528 | + | Int("events", count). | |
| 1529 | + | Str("phase", fmt.Sprint(event.Phase)). | |
| 1530 | + | Str("unit", fmt.Sprint(event.Unit)). | |
| 1531 | + | Bool("unit_known", event.UnitKnown). | |
| 1532 | + | Float64("dx_sum", dx). | |
| 1533 | + | Float64("dy_sum", dy). | |
| 1534 | + | Int64("cef_delta_x_sum", deltaX). | |
| 1535 | + | Int64("cef_delta_y_sum", deltaY). | |
| 1536 | + | Bool("nav_enabled", wv.inputConfig.TouchpadNavigationEnabled). | |
| 1537 | + | Bool("can_go_back", wv.CanGoBack()). | |
| 1538 | + | Bool("can_go_forward", wv.CanGoForward()). | |
| 1539 | + | Msg("cef: touchpad horizontal scroll diagnostic") | |
| 1540 | + | ||
| 1541 | + | return cef2gtk.ScrollForwardToCEF | |
| 1542 | + | } | |
| 1543 | + | ||
| 1544 | + | func (wv *WebView) handleNavigationSwipeAction(action cef2gtk.NavigationSwipeAction) { | |
| 1545 | + | logging.FromContext(wv.ctx).Debug(). | |
| 1546 | + | Uint64("webview_id", uint64(wv.id)). | |
| 1547 | + | Str("action", fmt.Sprint(action)). | |
| 1548 | + | Bool("can_go_back", wv.CanGoBack()). | |
| 1549 | + | Bool("can_go_forward", wv.CanGoForward()). | |
| 1550 | + | Msg("cef: touchpad navigation swipe recognized") | |
| 1551 | + | ||
| 1552 | + | switch action { | |
| 1553 | + | case cef2gtk.NavigationSwipeBack: | |
| 1554 | + | if err := wv.GoBack(wv.ctx); err != nil { | |
| 1555 | + | logging.FromContext(wv.ctx).Warn().Err(err).Msg("touchpad back navigation failed") | |
| 1556 | + | } | |
| 1557 | + | case cef2gtk.NavigationSwipeForward: | |
| 1558 | + | if err := wv.GoForward(wv.ctx); err != nil { | |
| 1559 | + | logging.FromContext(wv.ctx).Warn().Err(err).Msg("touchpad forward navigation failed") | |
| 1560 | + | } | |
| 1575 | 1561 | } | |
| 1576 | - | return touchpadNavigationNone | |
| 1577 | 1562 | } | |
| 1578 | 1563 | ||
| 1579 | 1564 | func (wv *WebView) viewBridgeScale() float64 { | |
| @@ -225,8 +225,16 @@ type WebView struct { | |||||
| 225 | windowlessFrameRate int32 | 225 | windowlessFrameRate int32 | ||
| 226 | backgroundColor uint32 | 226 | backgroundColor uint32 | ||
| 227 | inputConfig RuntimeInputConfig | 227 | inputConfig RuntimeInputConfig | ||
| 228 | - | touchpadSwipeDX float64 | 228 | + | |
| 229 | - | touchpadSwipeDY float64 | 229 | + | // Touchpad input diagnostics are debounced to avoid flooding logs while |
| 230 | + | // debugging continuous scroll streams. | |||
| 231 | + | inputDiagMu sync.Mutex | |||
| 232 | + | inputDiagLastLog time.Time | |||
| 233 | + | inputDiagEvents int | |||
| 234 | + | inputDiagDX float64 | |||
| 235 | + | inputDiagDY float64 | |||
| 236 | + | inputDiagDeltaXSum int64 | |||
| 237 | + | inputDiagDeltaYSum int64 | |||
| 230 | 238 | ||||
| 231 | // Audio output factory and active stream. | 239 | // Audio output factory and active stream. | ||
| 232 | audioOutputFactory port.AudioOutputFactory | 240 | audioOutputFactory port.AudioOutputFactory | ||
| @@ -1462,7 +1470,7 @@ func (wv *WebView) bridgeInputOptions() cef2gtk.InputOptions { | |||||
| 1462 | Scale: 0, | 1470 | Scale: 0, | ||
| 1463 | Scroll: cef2gtk.ScrollOptions{ | 1471 | Scroll: cef2gtk.ScrollOptions{ | ||
| 1464 | WheelMultiplier: wv.inputConfig.ScrollWheelMultiplier, | 1472 | WheelMultiplier: wv.inputConfig.ScrollWheelMultiplier, | ||
| 1465 | - | TouchpadMultiplier: wv.inputConfig.ScrollTouchpadMultiplier, | 1473 | + | PreciseMultiplier: wv.inputConfig.ScrollPreciseMultiplier, |
| 1466 | HorizontalMultiplier: wv.inputConfig.ScrollHorizontalMultiplier, | 1474 | HorizontalMultiplier: wv.inputConfig.ScrollHorizontalMultiplier, | ||
| 1467 | VerticalMultiplier: wv.inputConfig.ScrollVerticalMultiplier, | 1475 | VerticalMultiplier: wv.inputConfig.ScrollVerticalMultiplier, | ||
| 1468 | MaxDelta: wv.inputConfig.ScrollMaxDelta, | 1476 | MaxDelta: wv.inputConfig.ScrollMaxDelta, | ||
| @@ -1470,13 +1478,16 @@ func (wv *WebView) bridgeInputOptions() cef2gtk.InputOptions { | |||||
| 1470 | OnMiddleClick: func(_, _ float64) bool { | 1478 | OnMiddleClick: func(_, _ float64) bool { | ||
| 1471 | return wv.handleMiddleClickFromBridge() | 1479 | return wv.handleMiddleClickFromBridge() | ||
| 1472 | }, | 1480 | }, | ||
| 1473 | - | OnTouchpadSwipe: func(event cef2gtk.TouchpadSwipeEvent) cef2gtk.TouchpadSwipeDecision { | 1481 | + | OnScroll: wv.handleScrollInputDiagnostic, |
| 1474 | - | if wv.handleTouchpadNavigationSwipe(event) { | 1482 | + | NavigationSwipe: cef2gtk.NavigationSwipeOptions{ |
| 1475 | - | return cef2gtk.TouchpadSwipeConsume | 1483 | + | Enabled: wv.inputConfig.TouchpadNavigationEnabled, |
| 1476 | - | } | 1484 | + | MinDelta: wv.inputConfig.TouchpadNavigationMinDelta, |
| 1477 | - | return cef2gtk.TouchpadSwipePassthrough | 1485 | + | MaxVerticalRatio: wv.inputConfig.TouchpadNavigationMaxVerticalRatio, |
| 1478 | }, | 1486 | }, | ||
| 1479 | - | SelectionText: wv.selectedTextSnapshot, | 1487 | + | CanNavigateBack: wv.CanGoBack, |
| 1488 | + | CanNavigateForward: wv.CanGoForward, | |||
| 1489 | + | OnNavigateSwipe: wv.handleNavigationSwipeAction, | |||
| 1490 | + | SelectionText: wv.selectedTextSnapshot, | |||
| 1480 | OnClipboardShortcut: func(action, text string) { | 1491 | OnClipboardShortcut: func(action, text string) { | ||
| 1481 | if wv.engine != nil { | 1492 | if wv.engine != nil { | ||
| 1482 | wv.engine.handleExplicitClipboardBridgeText(wv.id, action, text) | 1493 | wv.engine.handleExplicitClipboardBridgeText(wv.id, action, text) | ||
| @@ -1485,95 +1496,69 @@ func (wv *WebView) bridgeInputOptions() cef2gtk.InputOptions { | |||||
| 1485 | } | 1496 | } | ||
| 1486 | } | 1497 | } | ||
| 1487 | 1498 | ||||
| 1488 | - | type touchpadNavigationAction int | 1499 | + | func (wv *WebView) handleScrollInputDiagnostic(event cef2gtk.ScrollEvent) cef2gtk.ScrollDecision { |
| 1489 | - | 1500 | + | if event.Phase != cef2gtk.ScrollPhaseUpdate || math.Abs(event.DX) == 0 { | |
| 1490 | - | const ( | 1501 | + | return cef2gtk.ScrollForwardToCEF |
| 1491 | - | touchpadNavigationNone touchpadNavigationAction = iota | |||
| 1492 | - | touchpadNavigationBack | |||
| 1493 | - | touchpadNavigationForward | |||
| 1494 | - | ) | |||
| 1495 | - | ||||
| 1496 | - | func (wv *WebView) handleTouchpadNavigationSwipe(event cef2gtk.TouchpadSwipeEvent) bool { | |||
| 1497 | - | cfg := wv.inputConfig | |||
| 1498 | - | if !cfg.TouchpadNavigationEnabled || event.Fingers != 2 { | |||
| 1499 | - | wv.resetTouchpadNavigationSwipe() | |||
| 1500 | - | return false | |||
| 1501 | } | 1502 | } | ||
| 1502 | 1503 | ||||
| 1503 | - | switch event.Phase { | 1504 | + | wv.inputDiagMu.Lock() |
| 1504 | - | case cef2gtk.TouchpadGesturePhaseBegin: | 1505 | + | wv.inputDiagEvents++ |
| 1505 | - | wv.resetTouchpadNavigationSwipe() | 1506 | + | wv.inputDiagDX += event.DX |
| 1506 | - | return false | 1507 | + | wv.inputDiagDY += event.DY |
| 1507 | - | case cef2gtk.TouchpadGesturePhaseUpdate: | 1508 | + | wv.inputDiagDeltaXSum += int64(event.DeltaX) |
| 1508 | - | wv.accumulateTouchpadNavigationSwipe(event.DX, event.DY) | 1509 | + | wv.inputDiagDeltaYSum += int64(event.DeltaY) |
| 1509 | - | return false | 1510 | + | now := time.Now() |
| 1510 | - | case cef2gtk.TouchpadGesturePhaseEnd: | 1511 | + | if !wv.inputDiagLastLog.IsZero() && now.Sub(wv.inputDiagLastLog) < time.Second { |
| 1511 | - | dx, dy := wv.finishTouchpadNavigationSwipe() | 1512 | + | wv.inputDiagMu.Unlock() |
| 1512 | - | switch chooseTouchpadNavigationAction(cfg, dx, dy, wv.CanGoBack(), wv.CanGoForward()) { | 1513 | + | return cef2gtk.ScrollForwardToCEF |
| 1513 | - | case touchpadNavigationBack: | 1514 | + | } |
| 1514 | - | if err := wv.GoBack(wv.ctx); err != nil { | 1515 | + | count := wv.inputDiagEvents |
| 1515 | - | logging.FromContext(wv.ctx).Warn().Err(err).Msg("touchpad back navigation failed") | 1516 | + | dx, dy := wv.inputDiagDX, wv.inputDiagDY |
| 1516 | - | return false | 1517 | + | deltaX, deltaY := wv.inputDiagDeltaXSum, wv.inputDiagDeltaYSum |
| 1517 | - | } | 1518 | + | wv.inputDiagEvents = 0 |
| 1518 | - | return true | 1519 | + | wv.inputDiagDX = 0 |
| 1519 | - | case touchpadNavigationForward: | 1520 | + | wv.inputDiagDY = 0 |
| 1520 | - | if err := wv.GoForward(wv.ctx); err != nil { | 1521 | + | wv.inputDiagDeltaXSum = 0 |
| 1521 | - | logging.FromContext(wv.ctx).Warn().Err(err).Msg("touchpad forward navigation failed") | 1522 | + | wv.inputDiagDeltaYSum = 0 |
| 1522 | - | return false | 1523 | + | wv.inputDiagLastLog = now |
| 1523 | - | } | 1524 | + | wv.inputDiagMu.Unlock() |
| 1524 | - | return true | |||
| 1525 | - | default: | |||
| 1526 | - | return false | |||
| 1527 | - | } | |||
| 1528 | - | case cef2gtk.TouchpadGesturePhaseCancel: | |||
| 1529 | - | wv.resetTouchpadNavigationSwipe() | |||
| 1530 | - | return false | |||
| 1531 | - | default: | |||
| 1532 | - | return false | |||
| 1533 | - | } | |||
| 1534 | - | } | |||
| 1535 | - | ||||
| 1536 | - | func (wv *WebView) resetTouchpadNavigationSwipe() { | |||
| 1537 | - | wv.mu.Lock() | |||
| 1538 | - | wv.touchpadSwipeDX = 0 | |||
| 1539 | - | wv.touchpadSwipeDY = 0 | |||
| 1540 | - | wv.mu.Unlock() | |||
| 1541 | - | } | |||
| 1542 | - | ||||
| 1543 | - | func (wv *WebView) accumulateTouchpadNavigationSwipe(dx, dy float64) { | |||
| 1544 | - | wv.mu.Lock() | |||
| 1545 | - | wv.touchpadSwipeDX += dx | |||
| 1546 | - | wv.touchpadSwipeDY += dy | |||
| 1547 | - | wv.mu.Unlock() | |||
| 1548 | - | } | |||
| 1549 | - | ||||
| 1550 | - | func (wv *WebView) finishTouchpadNavigationSwipe() (float64, float64) { | |||
| 1551 | - | wv.mu.Lock() | |||
| 1552 | - | defer wv.mu.Unlock() | |||
| 1553 | - | dx, dy := wv.touchpadSwipeDX, wv.touchpadSwipeDY | |||
| 1554 | - | wv.touchpadSwipeDX = 0 | |||
| 1555 | - | wv.touchpadSwipeDY = 0 | |||
| 1556 | - | return dx, dy | |||
| 1557 | - | } | |||
| 1558 | 1525 | ||||
| 1559 | - | func chooseTouchpadNavigationAction(cfg RuntimeInputConfig, dx, dy float64, canGoBack, canGoForward bool) touchpadNavigationAction { | 1526 | + | logging.FromContext(wv.ctx).Debug(). |
| 1560 | - | if !cfg.TouchpadNavigationEnabled { | 1527 | + | Uint64("webview_id", uint64(wv.id)). |
| 1561 | - | return touchpadNavigationNone | 1528 | + | Int("events", count). |
| 1562 | - | } | 1529 | + | Str("phase", fmt.Sprint(event.Phase)). |
| 1563 | - | absDX, absDY := math.Abs(dx), math.Abs(dy) | 1530 | + | Str("unit", fmt.Sprint(event.Unit)). |
| 1564 | - | if absDX < cfg.TouchpadNavigationMinDelta { | 1531 | + | Bool("unit_known", event.UnitKnown). |
| 1565 | - | return touchpadNavigationNone | 1532 | + | Float64("dx_sum", dx). |
| 1566 | - | } | 1533 | + | Float64("dy_sum", dy). |
| 1567 | - | if absDY > absDX*cfg.TouchpadNavigationMaxVerticalRatio { | 1534 | + | Int64("cef_delta_x_sum", deltaX). |
| 1568 | - | return touchpadNavigationNone | 1535 | + | Int64("cef_delta_y_sum", deltaY). |
| 1569 | - | } | 1536 | + | Bool("nav_enabled", wv.inputConfig.TouchpadNavigationEnabled). |
| 1570 | - | if dx > 0 && canGoBack { | 1537 | + | Bool("can_go_back", wv.CanGoBack()). |
| 1571 | - | return touchpadNavigationBack | 1538 | + | Bool("can_go_forward", wv.CanGoForward()). |
| 1572 | - | } | 1539 | + | Msg("cef: touchpad horizontal scroll diagnostic") |
| 1573 | - | if dx < 0 && canGoForward { | 1540 | + | |
| 1574 | - | return touchpadNavigationForward | 1541 | + | return cef2gtk.ScrollForwardToCEF |
| 1542 | + | } | |||
| 1543 | + | ||||
| 1544 | + | func (wv *WebView) handleNavigationSwipeAction(action cef2gtk.NavigationSwipeAction) { | |||
| 1545 | + | logging.FromContext(wv.ctx).Debug(). | |||
| 1546 | + | Uint64("webview_id", uint64(wv.id)). | |||
| 1547 | + | Str("action", fmt.Sprint(action)). | |||
| 1548 | + | Bool("can_go_back", wv.CanGoBack()). | |||
| 1549 | + | Bool("can_go_forward", wv.CanGoForward()). | |||
| 1550 | + | Msg("cef: touchpad navigation swipe recognized") | |||
| 1551 | + | ||||
| 1552 | + | switch action { | |||
| 1553 | + | case cef2gtk.NavigationSwipeBack: | |||
| 1554 | + | if err := wv.GoBack(wv.ctx); err != nil { | |||
| 1555 | + | logging.FromContext(wv.ctx).Warn().Err(err).Msg("touchpad back navigation failed") | |||
| 1556 | + | } | |||
| 1557 | + | case cef2gtk.NavigationSwipeForward: | |||
| 1558 | + | if err := wv.GoForward(wv.ctx); err != nil { | |||
| 1559 | + | logging.FromContext(wv.ctx).Warn().Err(err).Msg("touchpad forward navigation failed") | |||
| 1560 | + | } | |||
| 1575 | } | 1561 | } | ||
| 1576 | - | return touchpadNavigationNone | |||
| 1577 | } | 1562 | } | ||
| 1578 | 1563 | ||||
| 1579 | func (wv *WebView) viewBridgeScale() float64 { | 1564 | func (wv *WebView) viewBridgeScale() float64 { | ||
M internal/infrastructure/cef/webview_input_test.go +9 -40
| @@ -13,7 +13,7 @@ func TestWebViewBridgeInputOptions_LeavesTargetSelectionToAdapter(t *testing.T) | |||
| 13 | 13 | nativeWidget: nativeWidget, | |
| 14 | 14 | inputConfig: RuntimeInputConfig{ | |
| 15 | 15 | ScrollWheelMultiplier: 1.25, | |
| 16 | - | ScrollTouchpadMultiplier: 0.35, | |
| 16 | + | ScrollPreciseMultiplier: 2.5, | |
| 17 | 17 | ScrollHorizontalMultiplier: 0.75, | |
| 18 | 18 | ScrollVerticalMultiplier: 1.5, | |
| 19 | 19 | ScrollMaxDelta: 120, | |
| @@ -27,50 +27,19 @@ func TestWebViewBridgeInputOptions_LeavesTargetSelectionToAdapter(t *testing.T) | |||
| 27 | 27 | ||
| 28 | 28 | require.Zero(t, opts.Scale) | |
| 29 | 29 | require.InDelta(t, 1.25, opts.Scroll.WheelMultiplier, 0.001) | |
| 30 | - | require.InDelta(t, 0.35, opts.Scroll.TouchpadMultiplier, 0.001) | |
| 30 | + | require.InDelta(t, 2.5, opts.Scroll.PreciseMultiplier, 0.001) | |
| 31 | 31 | require.InDelta(t, 0.75, opts.Scroll.HorizontalMultiplier, 0.001) | |
| 32 | 32 | require.InDelta(t, 1.5, opts.Scroll.VerticalMultiplier, 0.001) | |
| 33 | 33 | require.Equal(t, int32(120), opts.Scroll.MaxDelta) | |
| 34 | 34 | require.NotNil(t, opts.OnMiddleClick) | |
| 35 | - | require.NotNil(t, opts.OnTouchpadSwipe) | |
| 35 | + | require.NotNil(t, opts.OnScroll) | |
| 36 | + | require.True(t, opts.NavigationSwipe.Enabled) | |
| 37 | + | require.InDelta(t, 80.0, opts.NavigationSwipe.MinDelta, 0.001) | |
| 38 | + | require.InDelta(t, 0.5, opts.NavigationSwipe.MaxVerticalRatio, 0.001) | |
| 39 | + | require.NotNil(t, opts.CanNavigateBack) | |
| 40 | + | require.NotNil(t, opts.CanNavigateForward) | |
| 41 | + | require.NotNil(t, opts.OnNavigateSwipe) | |
| 36 | 42 | require.NotNil(t, opts.SelectionText) | |
| 37 | 43 | require.NotNil(t, opts.OnClipboardShortcut) | |
| 38 | 44 | require.Same(t, nativeWidget, wv.nativeWidget) | |
| 39 | 45 | } | |
| 40 | - | ||
| 41 | - | func TestChooseTouchpadNavigationAction(t *testing.T) { | |
| 42 | - | cfg := RuntimeInputConfig{ | |
| 43 | - | TouchpadNavigationEnabled: true, | |
| 44 | - | TouchpadNavigationMinDelta: 80, | |
| 45 | - | TouchpadNavigationMaxVerticalRatio: 0.5, | |
| 46 | - | } | |
| 47 | - | ||
| 48 | - | tests := []struct { | |
| 49 | - | name string | |
| 50 | - | cfg *RuntimeInputConfig | |
| 51 | - | dx, dy float64 | |
| 52 | - | canGoBack bool | |
| 53 | - | canGoForward bool | |
| 54 | - | want touchpadNavigationAction | |
| 55 | - | }{ | |
| 56 | - | {name: "right swipe goes back", dx: 100, dy: 10, canGoBack: true, want: touchpadNavigationBack}, | |
| 57 | - | {name: "left swipe goes forward", dx: -100, dy: 10, canGoForward: true, want: touchpadNavigationForward}, | |
| 58 | - | {name: "below threshold ignored", dx: 40, dy: 0, canGoBack: true, want: touchpadNavigationNone}, | |
| 59 | - | {name: "disabled ignored", cfg: &RuntimeInputConfig{TouchpadNavigationEnabled: false, TouchpadNavigationMinDelta: 80, TouchpadNavigationMaxVerticalRatio: 0.5}, dx: 100, dy: 0, canGoBack: true, want: touchpadNavigationNone}, | |
| 60 | - | {name: "exact threshold goes back", dx: 80, dy: 0, canGoBack: true, want: touchpadNavigationBack}, | |
| 61 | - | {name: "vertical diagonal ignored", dx: 100, dy: 80, canGoBack: true, want: touchpadNavigationNone}, | |
| 62 | - | {name: "unavailable back ignored", dx: 100, dy: 0, canGoBack: false, want: touchpadNavigationNone}, | |
| 63 | - | {name: "unavailable forward ignored", dx: -100, dy: 0, canGoForward: false, want: touchpadNavigationNone}, | |
| 64 | - | } | |
| 65 | - | ||
| 66 | - | for _, tt := range tests { | |
| 67 | - | t.Run(tt.name, func(t *testing.T) { | |
| 68 | - | testCfg := cfg | |
| 69 | - | if tt.cfg != nil { | |
| 70 | - | testCfg = *tt.cfg | |
| 71 | - | } | |
| 72 | - | got := chooseTouchpadNavigationAction(testCfg, tt.dx, tt.dy, tt.canGoBack, tt.canGoForward) | |
| 73 | - | require.Equal(t, tt.want, got) | |
| 74 | - | }) | |
| 75 | - | } | |
| 76 | - | } | |
| @@ -13,7 +13,7 @@ func TestWebViewBridgeInputOptions_LeavesTargetSelectionToAdapter(t *testing.T) | |||||
| 13 | nativeWidget: nativeWidget, | 13 | nativeWidget: nativeWidget, | ||
| 14 | inputConfig: RuntimeInputConfig{ | 14 | inputConfig: RuntimeInputConfig{ | ||
| 15 | ScrollWheelMultiplier: 1.25, | 15 | ScrollWheelMultiplier: 1.25, | ||
| 16 | - | ScrollTouchpadMultiplier: 0.35, | 16 | + | ScrollPreciseMultiplier: 2.5, |
| 17 | ScrollHorizontalMultiplier: 0.75, | 17 | ScrollHorizontalMultiplier: 0.75, | ||
| 18 | ScrollVerticalMultiplier: 1.5, | 18 | ScrollVerticalMultiplier: 1.5, | ||
| 19 | ScrollMaxDelta: 120, | 19 | ScrollMaxDelta: 120, | ||
| @@ -27,50 +27,19 @@ func TestWebViewBridgeInputOptions_LeavesTargetSelectionToAdapter(t *testing.T) | |||||
| 27 | 27 | ||||
| 28 | require.Zero(t, opts.Scale) | 28 | require.Zero(t, opts.Scale) | ||
| 29 | require.InDelta(t, 1.25, opts.Scroll.WheelMultiplier, 0.001) | 29 | require.InDelta(t, 1.25, opts.Scroll.WheelMultiplier, 0.001) | ||
| 30 | - | require.InDelta(t, 0.35, opts.Scroll.TouchpadMultiplier, 0.001) | 30 | + | require.InDelta(t, 2.5, opts.Scroll.PreciseMultiplier, 0.001) |
| 31 | require.InDelta(t, 0.75, opts.Scroll.HorizontalMultiplier, 0.001) | 31 | require.InDelta(t, 0.75, opts.Scroll.HorizontalMultiplier, 0.001) | ||
| 32 | require.InDelta(t, 1.5, opts.Scroll.VerticalMultiplier, 0.001) | 32 | require.InDelta(t, 1.5, opts.Scroll.VerticalMultiplier, 0.001) | ||
| 33 | require.Equal(t, int32(120), opts.Scroll.MaxDelta) | 33 | require.Equal(t, int32(120), opts.Scroll.MaxDelta) | ||
| 34 | require.NotNil(t, opts.OnMiddleClick) | 34 | require.NotNil(t, opts.OnMiddleClick) | ||
| 35 | - | require.NotNil(t, opts.OnTouchpadSwipe) | 35 | + | require.NotNil(t, opts.OnScroll) |
| 36 | + | require.True(t, opts.NavigationSwipe.Enabled) | |||
| 37 | + | require.InDelta(t, 80.0, opts.NavigationSwipe.MinDelta, 0.001) | |||
| 38 | + | require.InDelta(t, 0.5, opts.NavigationSwipe.MaxVerticalRatio, 0.001) | |||
| 39 | + | require.NotNil(t, opts.CanNavigateBack) | |||
| 40 | + | require.NotNil(t, opts.CanNavigateForward) | |||
| 41 | + | require.NotNil(t, opts.OnNavigateSwipe) | |||
| 36 | require.NotNil(t, opts.SelectionText) | 42 | require.NotNil(t, opts.SelectionText) | ||
| 37 | require.NotNil(t, opts.OnClipboardShortcut) | 43 | require.NotNil(t, opts.OnClipboardShortcut) | ||
| 38 | require.Same(t, nativeWidget, wv.nativeWidget) | 44 | require.Same(t, nativeWidget, wv.nativeWidget) | ||
| 39 | } | 45 | } | ||
| 40 | - | ||||
| 41 | - | func TestChooseTouchpadNavigationAction(t *testing.T) { | |||
| 42 | - | cfg := RuntimeInputConfig{ | |||
| 43 | - | TouchpadNavigationEnabled: true, | |||
| 44 | - | TouchpadNavigationMinDelta: 80, | |||
| 45 | - | TouchpadNavigationMaxVerticalRatio: 0.5, | |||
| 46 | - | } | |||
| 47 | - | ||||
| 48 | - | tests := []struct { | |||
| 49 | - | name string | |||
| 50 | - | cfg *RuntimeInputConfig | |||
| 51 | - | dx, dy float64 | |||
| 52 | - | canGoBack bool | |||
| 53 | - | canGoForward bool | |||
| 54 | - | want touchpadNavigationAction | |||
| 55 | - | }{ | |||
| 56 | - | {name: "right swipe goes back", dx: 100, dy: 10, canGoBack: true, want: touchpadNavigationBack}, | |||
| 57 | - | {name: "left swipe goes forward", dx: -100, dy: 10, canGoForward: true, want: touchpadNavigationForward}, | |||
| 58 | - | {name: "below threshold ignored", dx: 40, dy: 0, canGoBack: true, want: touchpadNavigationNone}, | |||
| 59 | - | {name: "disabled ignored", cfg: &RuntimeInputConfig{TouchpadNavigationEnabled: false, TouchpadNavigationMinDelta: 80, TouchpadNavigationMaxVerticalRatio: 0.5}, dx: 100, dy: 0, canGoBack: true, want: touchpadNavigationNone}, | |||
| 60 | - | {name: "exact threshold goes back", dx: 80, dy: 0, canGoBack: true, want: touchpadNavigationBack}, | |||
| 61 | - | {name: "vertical diagonal ignored", dx: 100, dy: 80, canGoBack: true, want: touchpadNavigationNone}, | |||
| 62 | - | {name: "unavailable back ignored", dx: 100, dy: 0, canGoBack: false, want: touchpadNavigationNone}, | |||
| 63 | - | {name: "unavailable forward ignored", dx: -100, dy: 0, canGoForward: false, want: touchpadNavigationNone}, | |||
| 64 | - | } | |||
| 65 | - | ||||
| 66 | - | for _, tt := range tests { | |||
| 67 | - | t.Run(tt.name, func(t *testing.T) { | |||
| 68 | - | testCfg := cfg | |||
| 69 | - | if tt.cfg != nil { | |||
| 70 | - | testCfg = *tt.cfg | |||
| 71 | - | } | |||
| 72 | - | got := chooseTouchpadNavigationAction(testCfg, tt.dx, tt.dy, tt.canGoBack, tt.canGoForward) | |||
| 73 | - | require.Equal(t, tt.want, got) | |||
| 74 | - | }) | |||
| 75 | - | } | |||
| 76 | - | } | |||
M internal/infrastructure/config/defaults.go +11 -11
| @@ -67,16 +67,16 @@ const ( | |||
| 67 | 67 | defaultUIScale = 1.0 // UI scale multiplier (1.0 = 100%, 1.2 = 120%) | |
| 68 | 68 | ||
| 69 | 69 | // Performance defaults | |
| 70 | - | defaultZoomCacheSize = 256 // domains to cache (~20KB memory) | |
| 71 | - | defaultWebViewPoolPrewarmCount = 4 // WebViews to pre-create at startup | |
| 72 | - | defaultCEFWindowlessFrameRate = 60 // static OSR frame rate when adaptive CEF pacing is disabled | |
| 73 | - | defaultCEFWindowlessFrameRateMax = 240 // adaptive OSR frame-rate hard cap | |
| 74 | - | defaultCEFScrollMultiplier = 1.0 | |
| 75 | - | defaultCEFScrollTouchpadMultiplier = 0.35 | |
| 76 | - | defaultCEFScrollMaxDelta = 0 | |
| 77 | - | defaultCEFTouchpadNavigation = true | |
| 78 | - | defaultCEFTouchpadNavigationDelta = 80.0 | |
| 79 | - | defaultCEFTouchpadNavigationRatio = 0.5 | |
| 70 | + | defaultZoomCacheSize = 256 // domains to cache (~20KB memory) | |
| 71 | + | defaultWebViewPoolPrewarmCount = 4 // WebViews to pre-create at startup | |
| 72 | + | defaultCEFWindowlessFrameRate = 60 // static OSR frame rate when adaptive CEF pacing is disabled | |
| 73 | + | defaultCEFWindowlessFrameRateMax = 240 // adaptive OSR frame-rate hard cap | |
| 74 | + | defaultCEFScrollMultiplier = 1.0 | |
| 75 | + | defaultCEFScrollPreciseMultiplier = 2.5 | |
| 76 | + | defaultCEFScrollMaxDelta = 0 | |
| 77 | + | defaultCEFTouchpadNavigation = true | |
| 78 | + | defaultCEFTouchpadNavigationDelta = 15.0 | |
| 79 | + | defaultCEFTouchpadNavigationRatio = 0.5 | |
| 80 | 80 | ||
| 81 | 81 | // Skia threading defaults (0 = unset, -1 = unset for GPU threads) | |
| 82 | 82 | defaultSkiaCPUPaintingThreads = 0 | |
| @@ -186,7 +186,7 @@ func DefaultConfig() *Config { | |||
| 186 | 186 | EnableAudioHandler: true, | |
| 187 | 187 | Input: CEFInputConfig{ | |
| 188 | 188 | ScrollWheelMultiplier: defaultCEFScrollMultiplier, | |
| 189 | - | ScrollTouchpadMultiplier: defaultCEFScrollTouchpadMultiplier, | |
| 189 | + | ScrollPreciseMultiplier: defaultCEFScrollPreciseMultiplier, | |
| 190 | 190 | ScrollHorizontalMultiplier: defaultCEFScrollMultiplier, | |
| 191 | 191 | ScrollVerticalMultiplier: defaultCEFScrollMultiplier, | |
| 192 | 192 | ScrollMaxDelta: defaultCEFScrollMaxDelta, | |
| @@ -67,16 +67,16 @@ const ( | |||||
| 67 | defaultUIScale = 1.0 // UI scale multiplier (1.0 = 100%, 1.2 = 120%) | 67 | defaultUIScale = 1.0 // UI scale multiplier (1.0 = 100%, 1.2 = 120%) | ||
| 68 | 68 | ||||
| 69 | // Performance defaults | 69 | // Performance defaults | ||
| 70 | - | defaultZoomCacheSize = 256 // domains to cache (~20KB memory) | 70 | + | defaultZoomCacheSize = 256 // domains to cache (~20KB memory) |
| 71 | - | defaultWebViewPoolPrewarmCount = 4 // WebViews to pre-create at startup | 71 | + | defaultWebViewPoolPrewarmCount = 4 // WebViews to pre-create at startup |
| 72 | - | defaultCEFWindowlessFrameRate = 60 // static OSR frame rate when adaptive CEF pacing is disabled | 72 | + | defaultCEFWindowlessFrameRate = 60 // static OSR frame rate when adaptive CEF pacing is disabled |
| 73 | - | defaultCEFWindowlessFrameRateMax = 240 // adaptive OSR frame-rate hard cap | 73 | + | defaultCEFWindowlessFrameRateMax = 240 // adaptive OSR frame-rate hard cap |
| 74 | - | defaultCEFScrollMultiplier = 1.0 | 74 | + | defaultCEFScrollMultiplier = 1.0 |
| 75 | - | defaultCEFScrollTouchpadMultiplier = 0.35 | 75 | + | defaultCEFScrollPreciseMultiplier = 2.5 |
| 76 | - | defaultCEFScrollMaxDelta = 0 | 76 | + | defaultCEFScrollMaxDelta = 0 |
| 77 | - | defaultCEFTouchpadNavigation = true | 77 | + | defaultCEFTouchpadNavigation = true |
| 78 | - | defaultCEFTouchpadNavigationDelta = 80.0 | 78 | + | defaultCEFTouchpadNavigationDelta = 15.0 |
| 79 | - | defaultCEFTouchpadNavigationRatio = 0.5 | 79 | + | defaultCEFTouchpadNavigationRatio = 0.5 |
| 80 | 80 | ||||
| 81 | // Skia threading defaults (0 = unset, -1 = unset for GPU threads) | 81 | // Skia threading defaults (0 = unset, -1 = unset for GPU threads) | ||
| 82 | defaultSkiaCPUPaintingThreads = 0 | 82 | defaultSkiaCPUPaintingThreads = 0 | ||
| @@ -186,7 +186,7 @@ func DefaultConfig() *Config { | |||||
| 186 | EnableAudioHandler: true, | 186 | EnableAudioHandler: true, | ||
| 187 | Input: CEFInputConfig{ | 187 | Input: CEFInputConfig{ | ||
| 188 | ScrollWheelMultiplier: defaultCEFScrollMultiplier, | 188 | ScrollWheelMultiplier: defaultCEFScrollMultiplier, | ||
| 189 | - | ScrollTouchpadMultiplier: defaultCEFScrollTouchpadMultiplier, | 189 | + | ScrollPreciseMultiplier: defaultCEFScrollPreciseMultiplier, |
| 190 | ScrollHorizontalMultiplier: defaultCEFScrollMultiplier, | 190 | ScrollHorizontalMultiplier: defaultCEFScrollMultiplier, | ||
| 191 | ScrollVerticalMultiplier: defaultCEFScrollMultiplier, | 191 | ScrollVerticalMultiplier: defaultCEFScrollMultiplier, | ||
| 192 | ScrollMaxDelta: defaultCEFScrollMaxDelta, | 192 | ScrollMaxDelta: defaultCEFScrollMaxDelta, | ||
M internal/infrastructure/config/defaults_test.go +1 -1
| @@ -26,7 +26,7 @@ func TestDefaultConfig_CoreDefaults(t *testing.T) { | |||
| 26 | 26 | assert.True(t, cfg.Engine.CEF.EnableAudioHandler) | |
| 27 | 27 | assert.False(t, cfg.Engine.CEF.TraceHandlers) | |
| 28 | 28 | assert.InDelta(t, defaultCEFScrollMultiplier, cfg.Engine.CEF.Input.ScrollWheelMultiplier, 0.001) | |
| 29 | - | assert.InDelta(t, defaultCEFScrollTouchpadMultiplier, cfg.Engine.CEF.Input.ScrollTouchpadMultiplier, 0.001) | |
| 29 | + | assert.InDelta(t, defaultCEFScrollPreciseMultiplier, cfg.Engine.CEF.Input.ScrollPreciseMultiplier, 0.001) | |
| 30 | 30 | assert.InDelta(t, defaultCEFScrollMultiplier, cfg.Engine.CEF.Input.ScrollHorizontalMultiplier, 0.001) | |
| 31 | 31 | assert.InDelta(t, defaultCEFScrollMultiplier, cfg.Engine.CEF.Input.ScrollVerticalMultiplier, 0.001) | |
| 32 | 32 | assert.Equal(t, int32(defaultCEFScrollMaxDelta), cfg.Engine.CEF.Input.ScrollMaxDelta) | |
| @@ -26,7 +26,7 @@ func TestDefaultConfig_CoreDefaults(t *testing.T) { | |||||
| 26 | assert.True(t, cfg.Engine.CEF.EnableAudioHandler) | 26 | assert.True(t, cfg.Engine.CEF.EnableAudioHandler) | ||
| 27 | assert.False(t, cfg.Engine.CEF.TraceHandlers) | 27 | assert.False(t, cfg.Engine.CEF.TraceHandlers) | ||
| 28 | assert.InDelta(t, defaultCEFScrollMultiplier, cfg.Engine.CEF.Input.ScrollWheelMultiplier, 0.001) | 28 | assert.InDelta(t, defaultCEFScrollMultiplier, cfg.Engine.CEF.Input.ScrollWheelMultiplier, 0.001) | ||
| 29 | - | assert.InDelta(t, defaultCEFScrollTouchpadMultiplier, cfg.Engine.CEF.Input.ScrollTouchpadMultiplier, 0.001) | 29 | + | assert.InDelta(t, defaultCEFScrollPreciseMultiplier, cfg.Engine.CEF.Input.ScrollPreciseMultiplier, 0.001) |
| 30 | assert.InDelta(t, defaultCEFScrollMultiplier, cfg.Engine.CEF.Input.ScrollHorizontalMultiplier, 0.001) | 30 | assert.InDelta(t, defaultCEFScrollMultiplier, cfg.Engine.CEF.Input.ScrollHorizontalMultiplier, 0.001) | ||
| 31 | assert.InDelta(t, defaultCEFScrollMultiplier, cfg.Engine.CEF.Input.ScrollVerticalMultiplier, 0.001) | 31 | assert.InDelta(t, defaultCEFScrollMultiplier, cfg.Engine.CEF.Input.ScrollVerticalMultiplier, 0.001) | ||
| 32 | assert.Equal(t, int32(defaultCEFScrollMaxDelta), cfg.Engine.CEF.Input.ScrollMaxDelta) | 32 | assert.Equal(t, int32(defaultCEFScrollMaxDelta), cfg.Engine.CEF.Input.ScrollMaxDelta) | ||
M internal/infrastructure/config/engine.go +1 -1
| @@ -121,7 +121,7 @@ type CEFEngineConfig struct { | |||
| 121 | 121 | // CEFInputConfig holds CEF-specific input translation options. | |
| 122 | 122 | type CEFInputConfig struct { | |
| 123 | 123 | ScrollWheelMultiplier float64 `mapstructure:"scroll_wheel_multiplier" toml:"scroll_wheel_multiplier" yaml:"scroll_wheel_multiplier"` //nolint:lll // struct tags exceed lll limit | |
| 124 | - | ScrollTouchpadMultiplier float64 `mapstructure:"scroll_touchpad_multiplier" toml:"scroll_touchpad_multiplier" yaml:"scroll_touchpad_multiplier"` //nolint:lll // struct tags exceed lll limit | |
| 124 | + | ScrollPreciseMultiplier float64 `mapstructure:"scroll_precise_multiplier" toml:"scroll_precise_multiplier" yaml:"scroll_precise_multiplier"` //nolint:lll // struct tags exceed lll limit | |
| 125 | 125 | ScrollHorizontalMultiplier float64 `mapstructure:"scroll_horizontal_multiplier" toml:"scroll_horizontal_multiplier" yaml:"scroll_horizontal_multiplier"` //nolint:lll // struct tags exceed lll limit | |
| 126 | 126 | ScrollVerticalMultiplier float64 `mapstructure:"scroll_vertical_multiplier" toml:"scroll_vertical_multiplier" yaml:"scroll_vertical_multiplier"` //nolint:lll // struct tags exceed lll limit | |
| 127 | 127 | ScrollMaxDelta int32 `mapstructure:"scroll_max_delta" toml:"scroll_max_delta" yaml:"scroll_max_delta"` //nolint:lll // struct tags exceed lll limit | |
| @@ -121,7 +121,7 @@ type CEFEngineConfig struct { | |||||
| 121 | // CEFInputConfig holds CEF-specific input translation options. | 121 | // CEFInputConfig holds CEF-specific input translation options. | ||
| 122 | type CEFInputConfig struct { | 122 | type CEFInputConfig struct { | ||
| 123 | ScrollWheelMultiplier float64 `mapstructure:"scroll_wheel_multiplier" toml:"scroll_wheel_multiplier" yaml:"scroll_wheel_multiplier"` //nolint:lll // struct tags exceed lll limit | 123 | ScrollWheelMultiplier float64 `mapstructure:"scroll_wheel_multiplier" toml:"scroll_wheel_multiplier" yaml:"scroll_wheel_multiplier"` //nolint:lll // struct tags exceed lll limit | ||
| 124 | - | ScrollTouchpadMultiplier float64 `mapstructure:"scroll_touchpad_multiplier" toml:"scroll_touchpad_multiplier" yaml:"scroll_touchpad_multiplier"` //nolint:lll // struct tags exceed lll limit | 124 | + | ScrollPreciseMultiplier float64 `mapstructure:"scroll_precise_multiplier" toml:"scroll_precise_multiplier" yaml:"scroll_precise_multiplier"` //nolint:lll // struct tags exceed lll limit |
| 125 | ScrollHorizontalMultiplier float64 `mapstructure:"scroll_horizontal_multiplier" toml:"scroll_horizontal_multiplier" yaml:"scroll_horizontal_multiplier"` //nolint:lll // struct tags exceed lll limit | 125 | ScrollHorizontalMultiplier float64 `mapstructure:"scroll_horizontal_multiplier" toml:"scroll_horizontal_multiplier" yaml:"scroll_horizontal_multiplier"` //nolint:lll // struct tags exceed lll limit | ||
| 126 | ScrollVerticalMultiplier float64 `mapstructure:"scroll_vertical_multiplier" toml:"scroll_vertical_multiplier" yaml:"scroll_vertical_multiplier"` //nolint:lll // struct tags exceed lll limit | 126 | ScrollVerticalMultiplier float64 `mapstructure:"scroll_vertical_multiplier" toml:"scroll_vertical_multiplier" yaml:"scroll_vertical_multiplier"` //nolint:lll // struct tags exceed lll limit | ||
| 127 | ScrollMaxDelta int32 `mapstructure:"scroll_max_delta" toml:"scroll_max_delta" yaml:"scroll_max_delta"` //nolint:lll // struct tags exceed lll limit | 127 | ScrollMaxDelta int32 `mapstructure:"scroll_max_delta" toml:"scroll_max_delta" yaml:"scroll_max_delta"` //nolint:lll // struct tags exceed lll limit | ||
M internal/infrastructure/config/loader.go +1 -1
| @@ -783,7 +783,7 @@ func (m *Manager) setEngineDefaults(defaults *Config) { | |||
| 783 | 783 | ||
| 784 | 784 | func (m *Manager) setCEFInputDefaults(input CEFInputConfig) { | |
| 785 | 785 | m.viper.SetDefault("engine.cef.input.scroll_wheel_multiplier", input.ScrollWheelMultiplier) | |
| 786 | - | m.viper.SetDefault("engine.cef.input.scroll_touchpad_multiplier", input.ScrollTouchpadMultiplier) | |
| 786 | + | m.viper.SetDefault("engine.cef.input.scroll_precise_multiplier", input.ScrollPreciseMultiplier) | |
| 787 | 787 | m.viper.SetDefault("engine.cef.input.scroll_horizontal_multiplier", input.ScrollHorizontalMultiplier) | |
| 788 | 788 | m.viper.SetDefault("engine.cef.input.scroll_vertical_multiplier", input.ScrollVerticalMultiplier) | |
| 789 | 789 | m.viper.SetDefault("engine.cef.input.scroll_max_delta", input.ScrollMaxDelta) | |
| @@ -783,7 +783,7 @@ func (m *Manager) setEngineDefaults(defaults *Config) { | |||||
| 783 | 783 | ||||
| 784 | func (m *Manager) setCEFInputDefaults(input CEFInputConfig) { | 784 | func (m *Manager) setCEFInputDefaults(input CEFInputConfig) { | ||
| 785 | m.viper.SetDefault("engine.cef.input.scroll_wheel_multiplier", input.ScrollWheelMultiplier) | 785 | m.viper.SetDefault("engine.cef.input.scroll_wheel_multiplier", input.ScrollWheelMultiplier) | ||
| 786 | - | m.viper.SetDefault("engine.cef.input.scroll_touchpad_multiplier", input.ScrollTouchpadMultiplier) | 786 | + | m.viper.SetDefault("engine.cef.input.scroll_precise_multiplier", input.ScrollPreciseMultiplier) |
| 787 | m.viper.SetDefault("engine.cef.input.scroll_horizontal_multiplier", input.ScrollHorizontalMultiplier) | 787 | m.viper.SetDefault("engine.cef.input.scroll_horizontal_multiplier", input.ScrollHorizontalMultiplier) | ||
| 788 | m.viper.SetDefault("engine.cef.input.scroll_vertical_multiplier", input.ScrollVerticalMultiplier) | 788 | m.viper.SetDefault("engine.cef.input.scroll_vertical_multiplier", input.ScrollVerticalMultiplier) | ||
| 789 | m.viper.SetDefault("engine.cef.input.scroll_max_delta", input.ScrollMaxDelta) | 789 | m.viper.SetDefault("engine.cef.input.scroll_max_delta", input.ScrollMaxDelta) | ||
M internal/infrastructure/config/loader_test.go +1 -1
| @@ -18,7 +18,7 @@ func TestSetEngineDefaults(t *testing.T) { | |||
| 18 | 18 | assert.Equal(t, 4, mgr.viper.GetInt("engine.pool_prewarm_count")) | |
| 19 | 19 | assert.Equal(t, 256, mgr.viper.GetInt("engine.zoom_cache_size")) | |
| 20 | 20 | assert.InDelta(t, defaultCEFScrollMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_wheel_multiplier"), 0.001) | |
| 21 | - | assert.InDelta(t, defaultCEFScrollTouchpadMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_touchpad_multiplier"), 0.001) | |
| 21 | + | assert.InDelta(t, defaultCEFScrollPreciseMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_precise_multiplier"), 0.001) | |
| 22 | 22 | assert.InDelta(t, defaultCEFScrollMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_horizontal_multiplier"), 0.001) | |
| 23 | 23 | assert.InDelta(t, defaultCEFScrollMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_vertical_multiplier"), 0.001) | |
| 24 | 24 | assert.Equal(t, defaultCEFScrollMaxDelta, mgr.viper.GetInt("engine.cef.input.scroll_max_delta")) | |
| @@ -18,7 +18,7 @@ func TestSetEngineDefaults(t *testing.T) { | |||||
| 18 | assert.Equal(t, 4, mgr.viper.GetInt("engine.pool_prewarm_count")) | 18 | assert.Equal(t, 4, mgr.viper.GetInt("engine.pool_prewarm_count")) | ||
| 19 | assert.Equal(t, 256, mgr.viper.GetInt("engine.zoom_cache_size")) | 19 | assert.Equal(t, 256, mgr.viper.GetInt("engine.zoom_cache_size")) | ||
| 20 | assert.InDelta(t, defaultCEFScrollMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_wheel_multiplier"), 0.001) | 20 | assert.InDelta(t, defaultCEFScrollMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_wheel_multiplier"), 0.001) | ||
| 21 | - | assert.InDelta(t, defaultCEFScrollTouchpadMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_touchpad_multiplier"), 0.001) | 21 | + | assert.InDelta(t, defaultCEFScrollPreciseMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_precise_multiplier"), 0.001) |
| 22 | assert.InDelta(t, defaultCEFScrollMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_horizontal_multiplier"), 0.001) | 22 | assert.InDelta(t, defaultCEFScrollMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_horizontal_multiplier"), 0.001) | ||
| 23 | assert.InDelta(t, defaultCEFScrollMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_vertical_multiplier"), 0.001) | 23 | assert.InDelta(t, defaultCEFScrollMultiplier, mgr.viper.GetFloat64("engine.cef.input.scroll_vertical_multiplier"), 0.001) | ||
| 24 | assert.Equal(t, defaultCEFScrollMaxDelta, mgr.viper.GetInt("engine.cef.input.scroll_max_delta")) | 24 | assert.Equal(t, defaultCEFScrollMaxDelta, mgr.viper.GetInt("engine.cef.input.scroll_max_delta")) | ||
M internal/infrastructure/config/schema_provider.go +3 -3
| @@ -971,10 +971,10 @@ func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyIn | |||
| 971 | 971 | Section: SectionPerformance, | |
| 972 | 972 | }, | |
| 973 | 973 | { | |
| 974 | - | Key: "engine.cef.input.scroll_touchpad_multiplier", | |
| 974 | + | Key: "engine.cef.input.scroll_precise_multiplier", | |
| 975 | 975 | Type: "float64", | |
| 976 | - | Default: fmt.Sprintf("%.2f", defaults.Engine.CEF.Input.ScrollTouchpadMultiplier), | |
| 977 | - | Description: "CEF touchpad/smooth scroll delta multiplier", | |
| 976 | + | Default: fmt.Sprintf("%.2f", defaults.Engine.CEF.Input.ScrollPreciseMultiplier), | |
| 977 | + | Description: "CEF precise/surface scroll delta multiplier", | |
| 978 | 978 | Range: ">0", | |
| 979 | 979 | Section: SectionPerformance, | |
| 980 | 980 | }, | |
| @@ -971,10 +971,10 @@ func (*SchemaProvider) getPerformanceKeys(defaults *Config) []entity.ConfigKeyIn | |||||
| 971 | Section: SectionPerformance, | 971 | Section: SectionPerformance, | ||
| 972 | }, | 972 | }, | ||
| 973 | { | 973 | { | ||
| 974 | - | Key: "engine.cef.input.scroll_touchpad_multiplier", | 974 | + | Key: "engine.cef.input.scroll_precise_multiplier", |
| 975 | Type: "float64", | 975 | Type: "float64", | ||
| 976 | - | Default: fmt.Sprintf("%.2f", defaults.Engine.CEF.Input.ScrollTouchpadMultiplier), | 976 | + | Default: fmt.Sprintf("%.2f", defaults.Engine.CEF.Input.ScrollPreciseMultiplier), |
| 977 | - | Description: "CEF touchpad/smooth scroll delta multiplier", | 977 | + | Description: "CEF precise/surface scroll delta multiplier", |
| 978 | Range: ">0", | 978 | Range: ">0", | ||
| 979 | Section: SectionPerformance, | 979 | Section: SectionPerformance, | ||
| 980 | }, | 980 | }, | ||
M internal/infrastructure/config/validation.go +2 -2
| @@ -367,8 +367,8 @@ func validateCEF(config *Config) []string { | |||
| 367 | 367 | if invalidPositiveFloat(input.ScrollWheelMultiplier) { | |
| 368 | 368 | validationErrors = append(validationErrors, "engine.cef.input.scroll_wheel_multiplier must be > 0") | |
| 369 | 369 | } | |
| 370 | - | if invalidPositiveFloat(input.ScrollTouchpadMultiplier) { | |
| 371 | - | validationErrors = append(validationErrors, "engine.cef.input.scroll_touchpad_multiplier must be > 0") | |
| 370 | + | if invalidPositiveFloat(input.ScrollPreciseMultiplier) { | |
| 371 | + | validationErrors = append(validationErrors, "engine.cef.input.scroll_precise_multiplier must be > 0") | |
| 372 | 372 | } | |
| 373 | 373 | if invalidPositiveFloat(input.ScrollHorizontalMultiplier) { | |
| 374 | 374 | validationErrors = append(validationErrors, "engine.cef.input.scroll_horizontal_multiplier must be > 0") | |
| @@ -367,8 +367,8 @@ func validateCEF(config *Config) []string { | |||||
| 367 | if invalidPositiveFloat(input.ScrollWheelMultiplier) { | 367 | if invalidPositiveFloat(input.ScrollWheelMultiplier) { | ||
| 368 | validationErrors = append(validationErrors, "engine.cef.input.scroll_wheel_multiplier must be > 0") | 368 | validationErrors = append(validationErrors, "engine.cef.input.scroll_wheel_multiplier must be > 0") | ||
| 369 | } | 369 | } | ||
| 370 | - | if invalidPositiveFloat(input.ScrollTouchpadMultiplier) { | 370 | + | if invalidPositiveFloat(input.ScrollPreciseMultiplier) { |
| 371 | - | validationErrors = append(validationErrors, "engine.cef.input.scroll_touchpad_multiplier must be > 0") | 371 | + | validationErrors = append(validationErrors, "engine.cef.input.scroll_precise_multiplier must be > 0") |
| 372 | } | 372 | } | ||
| 373 | if invalidPositiveFloat(input.ScrollHorizontalMultiplier) { | 373 | if invalidPositiveFloat(input.ScrollHorizontalMultiplier) { | ||
| 374 | validationErrors = append(validationErrors, "engine.cef.input.scroll_horizontal_multiplier must be > 0") | 374 | validationErrors = append(validationErrors, "engine.cef.input.scroll_horizontal_multiplier must be > 0") | ||
M internal/infrastructure/config/validation_test.go +3 -3
| @@ -81,12 +81,12 @@ func TestValidateConfig_CEFConfig(t *testing.T) { | |||
| 81 | 81 | wantText: "engine.cef.input.scroll_wheel_multiplier", | |
| 82 | 82 | }, | |
| 83 | 83 | { | |
| 84 | - | name: "negative touchpad multiplier", | |
| 84 | + | name: "negative precise multiplier", | |
| 85 | 85 | mutate: func(cfg *Config) { | |
| 86 | - | cfg.Engine.CEF.Input.ScrollTouchpadMultiplier = -0.1 | |
| 86 | + | cfg.Engine.CEF.Input.ScrollPreciseMultiplier = -0.1 | |
| 87 | 87 | }, | |
| 88 | 88 | wantErr: true, | |
| 89 | - | wantText: "engine.cef.input.scroll_touchpad_multiplier", | |
| 89 | + | wantText: "engine.cef.input.scroll_precise_multiplier", | |
| 90 | 90 | }, | |
| 91 | 91 | { | |
| 92 | 92 | name: "zero horizontal multiplier", | |
| @@ -81,12 +81,12 @@ func TestValidateConfig_CEFConfig(t *testing.T) { | |||||
| 81 | wantText: "engine.cef.input.scroll_wheel_multiplier", | 81 | wantText: "engine.cef.input.scroll_wheel_multiplier", | ||
| 82 | }, | 82 | }, | ||
| 83 | { | 83 | { | ||
| 84 | - | name: "negative touchpad multiplier", | 84 | + | name: "negative precise multiplier", |
| 85 | mutate: func(cfg *Config) { | 85 | mutate: func(cfg *Config) { | ||
| 86 | - | cfg.Engine.CEF.Input.ScrollTouchpadMultiplier = -0.1 | 86 | + | cfg.Engine.CEF.Input.ScrollPreciseMultiplier = -0.1 |
| 87 | }, | 87 | }, | ||
| 88 | wantErr: true, | 88 | wantErr: true, | ||
| 89 | - | wantText: "engine.cef.input.scroll_touchpad_multiplier", | 89 | + | wantText: "engine.cef.input.scroll_precise_multiplier", |
| 90 | }, | 90 | }, | ||
| 91 | { | 91 | { | ||
| 92 | name: "zero horizontal multiplier", | 92 | name: "zero horizontal multiplier", | ||