kaxe 1.8.1.dev5__tar.gz → 1.8.1.dev6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. {kaxe-1.8.1.dev5/src/kaxe.egg-info → kaxe-1.8.1.dev6}/PKG-INFO +4 -2
  2. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/README.md +3 -1
  3. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/pyproject.toml +1 -1
  4. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/viewer/window.py +88 -9
  5. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6/src/kaxe.egg-info}/PKG-INFO +4 -2
  6. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/tests/test_viewer.py +30 -0
  7. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/LICENSE +0 -0
  8. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/MANIFEST.in +0 -0
  9. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/setup.cfg +0 -0
  10. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/__init__.py +0 -0
  11. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/__main__.py +0 -0
  12. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/_require_3d.py +0 -0
  13. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/_require_viewer.py +0 -0
  14. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/chart/__init__.py +0 -0
  15. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/chart/bar.py +0 -0
  16. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/chart/box.py +0 -0
  17. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/chart/pie.py +0 -0
  18. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/chart/qqplot.py +0 -0
  19. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/cli.py +0 -0
  20. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/__init__.py +0 -0
  21. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/axis.py +0 -0
  22. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/bounds.py +0 -0
  23. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/color.py +0 -0
  24. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/backend.py +0 -0
  25. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/camera.py +0 -0
  26. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/helper.py +0 -0
  27. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/hud.py +0 -0
  28. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/objects/__init__.py +0 -0
  29. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/objects/color.py +0 -0
  30. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/objects/line.py +0 -0
  31. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/objects/point.py +0 -0
  32. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/objects/pointer.py +0 -0
  33. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/objects/triangle.py +0 -0
  34. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/openglrender.py +0 -0
  35. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/d3/translator.py +0 -0
  36. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/draw.py +0 -0
  37. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/fileloader.py +0 -0
  38. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/helper.py +0 -0
  39. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/ipython_display.py +0 -0
  40. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/legend.py +0 -0
  41. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/line.py +0 -0
  42. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/marker.py +0 -0
  43. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/palette.py +0 -0
  44. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/profiler.py +0 -0
  45. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/progress.py +0 -0
  46. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/round.py +0 -0
  47. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/shapes.py +0 -0
  48. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/styles.py +0 -0
  49. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/svg.py +0 -0
  50. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/svg_pdf.py +0 -0
  51. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/symbol.py +0 -0
  52. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/text.py +0 -0
  53. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/thin.py +0 -0
  54. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/core/window.py +0 -0
  55. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/data/__init__.py +0 -0
  56. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/data/excel.py +0 -0
  57. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/__init__.py +0 -0
  58. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/_lazy.py +0 -0
  59. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/__init__.py +0 -0
  60. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/arrow.py +0 -0
  61. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/bubble.py +0 -0
  62. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/contour.py +0 -0
  63. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/equation.py +0 -0
  64. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/fill.py +0 -0
  65. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/function.py +0 -0
  66. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/guide_grid.py +0 -0
  67. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/inequality.py +0 -0
  68. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/map.py +0 -0
  69. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/parameter.py +0 -0
  70. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/pillar.py +0 -0
  71. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d2/point.py +0 -0
  72. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d3/__init__.py +0 -0
  73. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d3/base.py +0 -0
  74. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d3/function.py +0 -0
  75. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d3/mesh.py +0 -0
  76. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d3/point.py +0 -0
  77. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/d3/potato.py +0 -0
  78. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/function.py +0 -0
  79. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/legend.py +0 -0
  80. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/mapdata.py +0 -0
  81. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/point.py +0 -0
  82. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/objects/text.py +0 -0
  83. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/__init__.py +0 -0
  84. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/_lazy.py +0 -0
  85. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/box.py +0 -0
  86. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/constants.py +0 -0
  87. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/d3/__init__.py +0 -0
  88. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/d3/axes.py +0 -0
  89. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/d3/geometry.py +0 -0
  90. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/d3/plot3d.py +0 -0
  91. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/d3/variants.py +0 -0
  92. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/double.py +0 -0
  93. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/empty.py +0 -0
  94. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/grid.py +0 -0
  95. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/log.py +0 -0
  96. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/polar.py +0 -0
  97. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/standard.py +0 -0
  98. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/themes.py +0 -0
  99. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/zoom.py +0 -0
  100. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/plot/zoom_connector.py +0 -0
  101. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/project/__init__.py +0 -0
  102. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/project/codec.py +0 -0
  103. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/project/context.py +0 -0
  104. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/project/document.py +0 -0
  105. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/project/registry.py +0 -0
  106. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/project/sample.py +0 -0
  107. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/project/sampled_curve.py +0 -0
  108. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/project/serializers.py +0 -0
  109. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/project/window.py +0 -0
  110. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/__init__.py +0 -0
  111. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/__init__.py +0 -0
  112. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.bright-oblique.ttf +0 -0
  113. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.bright-roman.ttf +0 -0
  114. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.bright-semibold.ttf +0 -0
  115. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.bright-semiboldoblique.ttf +0 -0
  116. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.classical-serif-italic.ttf +0 -0
  117. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.concrete-bold.ttf +0 -0
  118. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.concrete-bolditalic.ttf +0 -0
  119. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.concrete-italic.ttf +0 -0
  120. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.concrete-roman.ttf +0 -0
  121. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-bold.ttf +0 -0
  122. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-boldoblique.ttf +0 -0
  123. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-demi-condensed-demicondensed.ttf +0 -0
  124. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-medium.ttf +0 -0
  125. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-oblique.ttf +0 -0
  126. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.serif-bold.ttf +0 -0
  127. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.serif-bolditalic.ttf +0 -0
  128. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-boldslanted.ttf +0 -0
  129. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-romanslanted.ttf +0 -0
  130. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.serif-italic.ttf +0 -0
  131. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.serif-roman.ttf +0 -0
  132. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.serif-upright-italic-uprightitalic.ttf +0 -0
  133. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bold.ttf +0 -0
  134. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bolditalic.ttf +0 -0
  135. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-italic.ttf +0 -0
  136. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-light.ttf +0 -0
  137. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-lightoblique.ttf +0 -0
  138. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-regular.ttf +0 -0
  139. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-italic.ttf +0 -0
  140. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-medium.ttf +0 -0
  141. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/computer-modern-family/readme.txt +0 -0
  142. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/resources/logo-small.png +0 -0
  143. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/viewer/__init__.py +0 -0
  144. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/viewer/cli.py +0 -0
  145. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/viewer/client.py +0 -0
  146. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe/viewer/server.py +0 -0
  147. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe.egg-info/SOURCES.txt +0 -0
  148. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe.egg-info/dependency_links.txt +0 -0
  149. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe.egg-info/entry_points.txt +0 -0
  150. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe.egg-info/requires.txt +0 -0
  151. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/src/kaxe.egg-info/top_level.txt +0 -0
  152. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/tests/test.py +0 -0
  153. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/tests/test2.py +0 -0
  154. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/tests/test3.py +0 -0
  155. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/tests/test_4.py +0 -0
  156. {kaxe-1.8.1.dev5 → kaxe-1.8.1.dev6}/tests/test_5.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaxe
3
- Version: 1.8.1.dev5
3
+ Version: 1.8.1.dev6
4
4
  Summary: A small graphing tool for functions, points, equations and more
5
5
  Author-email: Valter Yde Daugberg <valteryde@hotmail.com>
6
6
  Project-URL: Homepage, https://github.com/valteryde/kaxe
@@ -87,7 +87,9 @@ When the viewer is open, `.show()` instantly streams plots to cards on your canv
87
87
  **Canvas & Card Controls:**
88
88
  * **Click & Drag on a Card**: Move that card freely across the canvas
89
89
  * **Click & Drag on Empty Space**: Pan the camera across the workspace
90
- * **Mouse Scroll**: Smooth zoom anchored to cursor
90
+ * **Two-Finger Scroll / Trackpad**: Pan canvas side-to-side and up/down without zooming
91
+ * **`[Shift]` + Scroll**: Pan horizontally (for 1D mouse wheels)
92
+ * **`[Cmd]` / `[Ctrl]` + Scroll**: Smooth zoom anchored to cursor
91
93
  * **`[A]`**: Auto-arrange all cards in a neat row
92
94
  * **`[Space]` or `[R]`**: Fit all cards on the canvas into view
93
95
  * **`[1]`**: View canvas at 100% native pixel scale
@@ -54,7 +54,9 @@ When the viewer is open, `.show()` instantly streams plots to cards on your canv
54
54
  **Canvas & Card Controls:**
55
55
  * **Click & Drag on a Card**: Move that card freely across the canvas
56
56
  * **Click & Drag on Empty Space**: Pan the camera across the workspace
57
- * **Mouse Scroll**: Smooth zoom anchored to cursor
57
+ * **Two-Finger Scroll / Trackpad**: Pan canvas side-to-side and up/down without zooming
58
+ * **`[Shift]` + Scroll**: Pan horizontally (for 1D mouse wheels)
59
+ * **`[Cmd]` / `[Ctrl]` + Scroll**: Smooth zoom anchored to cursor
58
60
  * **`[A]`**: Auto-arrange all cards in a neat row
59
61
  * **`[Space]` or `[R]`**: Fit all cards on the canvas into view
60
62
  * **`[1]`**: View canvas at 100% native pixel scale
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kaxe"
3
- version = "1.8.1.dev5"
3
+ version = "1.8.1.dev6"
4
4
  authors = [
5
5
  { name="Valter Yde Daugberg", email="valteryde@hotmail.com" },
6
6
  ]
@@ -277,6 +277,11 @@ class KaxeViewerWindow(pyglet.window.Window):
277
277
  self.pan_camera_start_x: float = 0.0
278
278
  self.pan_camera_start_y: float = 0.0
279
279
 
280
+ # Key state handler for live modifier tracking (Cmd, Ctrl, Shift)
281
+ self.keys = key.KeyStateHandler()
282
+ self.push_handlers(self.keys)
283
+ self.current_modifiers: int = 0
284
+
280
285
  self.show_hud: bool = True
281
286
  self.notification_text: str = ""
282
287
  self.notification_expiry: float = 0.0
@@ -307,7 +312,7 @@ class KaxeViewerWindow(pyglet.window.Window):
307
312
  )
308
313
 
309
314
  self.hud_right = pyglet.text.Label(
310
- "[Drag] Move card / Pan [Scroll] Zoom [A] Arrange [←/→] Version [C] Copy [Space] Fit",
315
+ "[Drag] Move card / Pan [Cmd+Scroll] Zoom [Scroll] Pan [A] Arrange [Space] Fit",
311
316
  font_size=11,
312
317
  x=self.width - 15,
313
318
  y=15,
@@ -489,19 +494,90 @@ class KaxeViewerWindow(pyglet.window.Window):
489
494
  self.dragging_card = None
490
495
  self.is_panning_camera = False
491
496
 
497
+ def is_zoom_modifier_pressed(self) -> bool:
498
+ """Return True if Meta (Command) or Control key is held down."""
499
+ if sys.platform == "darwin":
500
+ try:
501
+ from pyglet.libs.darwin import cocoapy
502
+ flags = cocoapy.NSEvent.modifierFlags()
503
+ if flags & (cocoapy.NSCommandKeyMask | cocoapy.NSControlKeyMask):
504
+ return True
505
+ except Exception:
506
+ pass
507
+
508
+ if hasattr(self, "keys") and self.keys is not None:
509
+ if (
510
+ self.keys[key.LCOMMAND]
511
+ or self.keys[key.RCOMMAND]
512
+ or self.keys[key.LCTRL]
513
+ or self.keys[key.RCTRL]
514
+ ):
515
+ return True
516
+
517
+ if getattr(self, "current_modifiers", 0) & (key.MOD_COMMAND | key.MOD_CTRL):
518
+ return True
519
+
520
+ return False
521
+
522
+ def is_shift_pressed(self) -> bool:
523
+ """Return True if Shift key is held down."""
524
+ if sys.platform == "darwin":
525
+ try:
526
+ from pyglet.libs.darwin import cocoapy
527
+ flags = cocoapy.NSEvent.modifierFlags()
528
+ if flags & cocoapy.NSShiftKeyMask:
529
+ return True
530
+ except Exception:
531
+ pass
532
+
533
+ if hasattr(self, "keys") and self.keys is not None:
534
+ if self.keys[key.LSHIFT] or self.keys[key.RSHIFT]:
535
+ return True
536
+
537
+ if getattr(self, "current_modifiers", 0) & key.MOD_SHIFT:
538
+ return True
539
+
540
+ return False
541
+
492
542
  def on_mouse_scroll(self, x: int, y: int, scroll_x: float, scroll_y: float):
493
- """Cursor-anchored zooming on the canvas."""
494
- zoom_factor = 1.05 ** (-scroll_y)
495
- new_zoom = max(0.04, min(self.zoom * zoom_factor, 20.0))
543
+ """Scroll handler:
496
544
 
497
- if hasattr(self, "width"):
498
- wx, wy = self.screen_to_world(x, y)
499
- self.camera_x = wx - (x - self.width / 2.0) / new_zoom
500
- self.camera_y = wy - (y - self.height / 2.0) / new_zoom
545
+ - If Meta (Cmd) or Control is held: ZOOM anchored at cursor.
546
+ - Otherwise: PAN canvas side-to-side and up/down without zooming.
547
+ """
548
+ if hasattr(self, "keys") and self.keys is not None:
549
+ should_zoom = self.is_zoom_modifier_pressed()
550
+ else:
551
+ # Fallback for mock unit test instances
552
+ should_zoom = True
501
553
 
502
- self.zoom = new_zoom
554
+ if should_zoom:
555
+ zoom_factor = 1.05 ** (-scroll_y)
556
+ new_zoom = max(0.04, min(self.zoom * zoom_factor, 25.0))
557
+
558
+ if hasattr(self, "width"):
559
+ wx, wy = self.screen_to_world(x, y)
560
+ self.camera_x = wx - (x - self.width / 2.0) / new_zoom
561
+ self.camera_y = wy - (y - self.height / 2.0) / new_zoom
562
+
563
+ self.zoom = new_zoom
564
+ else:
565
+ # Pan without zooming
566
+ dx = scroll_x
567
+ dy = scroll_y
568
+
569
+ # Holding Shift converts vertical wheel into horizontal pan
570
+ if self.is_shift_pressed() and dy != 0.0 and dx == 0.0:
571
+ dx = dy
572
+ dy = 0.0
573
+
574
+ pan_speed = 20.0
575
+ self.camera_x -= (dx * pan_speed) / self.zoom
576
+ self.camera_y += (dy * pan_speed) / self.zoom
503
577
 
504
578
  def on_key_press(self, symbol: int, modifiers: int):
579
+ self.current_modifiers = modifiers
580
+
505
581
  # Fit all
506
582
  if symbol in (key.R, key.SPACE):
507
583
  self.fit_all_cards()
@@ -537,6 +613,9 @@ class KaxeViewerWindow(pyglet.window.Window):
537
613
  elif symbol == key.S:
538
614
  self.save_selected_card()
539
615
 
616
+ def on_key_release(self, symbol: int, modifiers: int):
617
+ self.current_modifiers = modifiers
618
+
540
619
  def copy_selected_card(self):
541
620
  if not self.selected_card or not self.selected_card.current_version:
542
621
  return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaxe
3
- Version: 1.8.1.dev5
3
+ Version: 1.8.1.dev6
4
4
  Summary: A small graphing tool for functions, points, equations and more
5
5
  Author-email: Valter Yde Daugberg <valteryde@hotmail.com>
6
6
  Project-URL: Homepage, https://github.com/valteryde/kaxe
@@ -87,7 +87,9 @@ When the viewer is open, `.show()` instantly streams plots to cards on your canv
87
87
  **Canvas & Card Controls:**
88
88
  * **Click & Drag on a Card**: Move that card freely across the canvas
89
89
  * **Click & Drag on Empty Space**: Pan the camera across the workspace
90
- * **Mouse Scroll**: Smooth zoom anchored to cursor
90
+ * **Two-Finger Scroll / Trackpad**: Pan canvas side-to-side and up/down without zooming
91
+ * **`[Shift]` + Scroll**: Pan horizontally (for 1D mouse wheels)
92
+ * **`[Cmd]` / `[Ctrl]` + Scroll**: Smooth zoom anchored to cursor
91
93
  * **`[A]`**: Auto-arrange all cards in a neat row
92
94
  * **`[Space]` or `[R]`**: Fit all cards on the canvas into view
93
95
  * **`[1]`**: View canvas at 100% native pixel scale
@@ -220,6 +220,36 @@ def test_viewer_zoom_reversed_and_less_sensitive():
220
220
  assert abs(window.zoom - 1.05) < 1e-6
221
221
 
222
222
 
223
+ def test_scroll_pans_side_to_side_without_zoom_modifier():
224
+ from unittest.mock import MagicMock
225
+ from kaxe.viewer.window import KaxeViewerWindow
226
+ from pyglet.window import key
227
+
228
+ window = object.__new__(KaxeViewerWindow)
229
+ window.keys = {key.LCOMMAND: False, key.RCOMMAND: False, key.LCTRL: False, key.RCTRL: False, key.LSHIFT: False, key.RSHIFT: False}
230
+ window.current_modifiers = 0
231
+ window.camera_x = 0.0
232
+ window.camera_y = 0.0
233
+ window.zoom = 1.0
234
+
235
+ # Horizontal scroll without Cmd/Ctrl -> pans horizontally, zoom stays unchanged!
236
+ window.on_mouse_scroll(x=400, y=300, scroll_x=2.0, scroll_y=0.0)
237
+ assert window.zoom == 1.0
238
+ assert window.camera_x < 0.0 # Camera moved left
239
+
240
+ # Vertical scroll without Cmd/Ctrl -> pans vertically, zoom stays unchanged!
241
+ orig_cam_x = window.camera_x
242
+ window.on_mouse_scroll(x=400, y=300, scroll_x=0.0, scroll_y=3.0)
243
+ assert window.zoom == 1.0
244
+ assert window.camera_x == orig_cam_x
245
+ assert window.camera_y > 0.0
246
+
247
+ # With Cmd key pressed -> zooms!
248
+ window.keys[key.LCOMMAND] = True
249
+ window.on_mouse_scroll(x=400, y=300, scroll_x=0.0, scroll_y=-1.0)
250
+ assert window.zoom > 1.0
251
+
252
+
223
253
  def test_plot_id_chaining_and_streaming(monkeypatch):
224
254
  import kaxe
225
255
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes