DefaultNavigation.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /**
  2. * Copyright (c) 2011-2018 by Andrew Mustun. All rights reserved.
  3. *
  4. * This file is part of the QCAD project.
  5. *
  6. * QCAD is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * QCAD is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with QCAD.
  18. */
  19. include("../../EAction.js");
  20. function DefaultNavigation(widget) {
  21. RActionAdapter.call(this);
  22. this.lastCursor = undefined;
  23. this.view = undefined;
  24. this.panning = false;
  25. this.savedCursor = undefined;
  26. this.panOrigin = new RVector();
  27. this.panFirstOrigin = new RVector();
  28. if (!isNull(widget)) {
  29. this.hruler = widget.findChild("HorizontalRuler");
  30. this.vruler = widget.findChild("VerticalRuler");
  31. }
  32. // make sure wheel behavior / pan gesture is initialized:
  33. DefaultNavigation.applyPreferences();
  34. }
  35. DefaultNavigation.prototype = new RActionAdapter();
  36. DefaultNavigation.WheelBehavior = {
  37. Zoom : 0,
  38. Pan : 1
  39. };
  40. DefaultNavigation.getPreferencesCategory = function() {
  41. return [qsTr("Graphics View"), qsTr("Navigation")];
  42. };
  43. DefaultNavigation.initPreferences = function(pageWidget, calledByPrefDialog, document) {
  44. var wheelCombo = pageWidget.findChild("Wheel");
  45. wheelCombo.addItem(qsTr("Zoom (Wheel Mouse)"), DefaultNavigation.WheelBehavior.Zoom);
  46. wheelCombo.addItem(qsTr("Scroll (Trackpad, Multi-Touch Mouse)"), DefaultNavigation.WheelBehavior.Pan);
  47. };
  48. DefaultNavigation.applyPreferences = function(doc) {
  49. DefaultNavigation.wheelBehavior = RSettings.getIntValue("GraphicsViewNavigation/Wheel", DefaultNavigation.WheelBehavior.Zoom);
  50. DefaultNavigation.reverseMouseWheelZoom = RSettings.getBoolValue("GraphicsViewNavigation/ReverseMouseWheelZoom", false);
  51. DefaultNavigation.mouseWheelZoomFactor = RSettings.getDoubleValue("GraphicsViewNavigation/MouseWheelZoomFactor", 1.2);
  52. if (RMath.fuzzyCompare(DefaultNavigation.mouseWheelZoomFactor, 0.0, 0.001)) {
  53. DefaultNavigation.mouseWheelZoomFactor = 1.2;
  54. }
  55. DefaultNavigation.panGesture = RSettings.getBoolValue("GraphicsViewNavigation/PanGesture", false);
  56. DefaultNavigation.middleMouseButtonZoomFactor = RSettings.getDoubleValue("GraphicsViewNavigation/MiddleMouseButtonZoomFactor", 1.2);
  57. DefaultNavigation.panThreshold = RSettings.getDoubleValue("GraphicsViewNavigation/PanThreshold", 4);
  58. };
  59. DefaultNavigation.prototype.beginEvent = function() {
  60. };
  61. DefaultNavigation.prototype.setGraphicsView = function(v) {
  62. this.view = v;
  63. };
  64. DefaultNavigation.prototype.finishEvent = function() {
  65. };
  66. /**
  67. * Implement panning with arrow keys.
  68. * Only has an effect if no actions are triggered using arrows as shortcuts.
  69. */
  70. //DefaultNavigation.prototype.keyPressEvent = function(event) {
  71. // var v = undefined;
  72. // if (event.isAccepted()) {
  73. // return;
  74. // }
  75. // if (event.key()===Qt.Key_Left.valueOf()) {
  76. // v = new RVector(50, 0);
  77. // } else if (event.key()===Qt.Key_Right.valueOf()) {
  78. // v = new RVector(-50, 0);
  79. // } else if (event.key()===Qt.Key_Up.valueOf()) {
  80. // v = new RVector(0, 50);
  81. // } else if (event.key()===Qt.Key_Down.valueOf()) {
  82. // v = new RVector(0, -50);
  83. // }
  84. // if (!isNull(v)) {
  85. // this.view.pan(v);
  86. // event.accept();
  87. // }
  88. //};
  89. DefaultNavigation.prototype.mousePressEvent = function(event) {
  90. if (isNull(this.view)) {
  91. return;
  92. }
  93. // middle button or left button and control (command on mac) to pan:
  94. if (event.button() === Qt.MidButton ||
  95. (event.button() === Qt.LeftButton &&
  96. event.modifiers().valueOf() === Qt.ControlModifier.valueOf())) {
  97. this.panOrigin = event.getScreenPosition();
  98. this.panFirstOrigin = this.panOrigin;
  99. this.panning = true;
  100. this.savedCursor = this.view.getCursor();
  101. this.view.startPan();
  102. EAction.disableCoordinateWidget();
  103. }
  104. };
  105. DefaultNavigation.prototype.mouseReleaseEvent = function(event) {
  106. if (isNull(this.view)) {
  107. return;
  108. }
  109. if (this.panning === true &&
  110. (event.button() === Qt.MidButton ||
  111. event.button() === Qt.LeftButton)) {
  112. if (event.button() === Qt.MidButton) {
  113. // only middle mouse button to zoom:
  114. this.handleMiddleMouseButtonZoom(event);
  115. }
  116. if (!isNull(this.savedCursor)) {
  117. this.view.setCursor(this.savedCursor);
  118. }
  119. this.panning = false;
  120. this.savedCursor = undefined;
  121. EAction.enableCoordinateWidget();
  122. }
  123. };
  124. DefaultNavigation.prototype.handleMiddleMouseButtonZoom = function(event) {
  125. if (this.panFirstOrigin.getDistanceTo(event.getScreenPosition())<DefaultNavigation.panThreshold) {
  126. // zoom in / out:
  127. var position = event.getModelPosition();
  128. if (event.modifiers().valueOf() === Qt.ShiftModifier.valueOf()) {
  129. this.view.zoomOut(position, DefaultNavigation.middleMouseButtonZoomFactor);
  130. }
  131. else {
  132. this.view.zoomIn(position, DefaultNavigation.middleMouseButtonZoomFactor);
  133. }
  134. }
  135. };
  136. DefaultNavigation.prototype.mouseMoveEvent = function(event) {
  137. if (isNull(this.view)) {
  138. return;
  139. }
  140. if (event.buttons().valueOf() & Qt.MidButton.valueOf() ||
  141. (event.buttons().valueOf() & Qt.LeftButton.valueOf() &&
  142. event.modifiers().valueOf() === Qt.ControlModifier.valueOf())) {
  143. if (this.panning === true) {
  144. var panTarget = event.getScreenPosition();
  145. var panDelta = panTarget.operator_subtract(this.panOrigin);
  146. if (Math.abs(panDelta.x) > DefaultNavigation.panThreshold || Math.abs(panDelta.y) > DefaultNavigation.panThreshold) {
  147. this.view.setCursor(
  148. new QCursor(Qt.ClosedHandCursor)
  149. );
  150. this.view.pan(panDelta);
  151. this.panOrigin = panTarget;
  152. }
  153. }
  154. }
  155. if (!isNull(this.hruler)) {
  156. this.hruler.update();
  157. }
  158. if (!isNull(this.vruler)) {
  159. this.vruler.update();
  160. }
  161. };
  162. DefaultNavigation.prototype.wheelEvent = function(event) {
  163. if (isNull(this.view)) {
  164. return;
  165. }
  166. var wheelDelta;
  167. if (isFunction(event.delta)) {
  168. wheelDelta = event.delta();
  169. }
  170. else {
  171. // Qt 6:
  172. wheelDelta = event.pixelDelta.y();
  173. }
  174. switch (event.modifiers().valueOf()) {
  175. // scroll up / down:
  176. case Qt.ControlModifier.valueOf():
  177. this.view.pan(new RVector(0, wheelDelta / 2));
  178. this.view.simulateMouseMoveEvent();
  179. break;
  180. // scroll left / right:
  181. case Qt.ShiftModifier.valueOf():
  182. this.view.pan(new RVector(wheelDelta / 2, 0));
  183. this.view.simulateMouseMoveEvent();
  184. break;
  185. // zoom in / out:
  186. case Qt.NoModifier.valueOf():
  187. // wheel behavior is "zoom":
  188. if (DefaultNavigation.wheelBehavior===DefaultNavigation.WheelBehavior.Zoom) {
  189. var position = event.getModelPosition();
  190. if (wheelDelta!==0) {
  191. if ((wheelDelta > 0) !== DefaultNavigation.reverseMouseWheelZoom) {
  192. this.view.zoom(position, DefaultNavigation.mouseWheelZoomFactor);
  193. }
  194. else {
  195. this.view.zoom(position, 1.0/DefaultNavigation.mouseWheelZoomFactor);
  196. }
  197. }
  198. }
  199. // wheel behavior is "pan":
  200. else {
  201. if (isNull(this.panOffset)) {
  202. this.panOffset = new RVector(0,0);
  203. }
  204. if (RSettings.getQtVersion()>=0x060000) {
  205. if (Math.abs(event.pixelDelta.y())>Math.abs(event.pixelDelta.x())) {
  206. // vertical:
  207. this.panOffset = this.panOffset.operator_add(new RVector(0, wheelDelta/2));
  208. }
  209. else {
  210. this.panOffset = this.panOffset.operator_add(new RVector(wheelDelta/2, 0));
  211. }
  212. }
  213. else {
  214. if (event.orientation()===Qt.Vertical) {
  215. this.panOffset = this.panOffset.operator_add(new RVector(0, wheelDelta/2));
  216. }
  217. else {
  218. this.panOffset = this.panOffset.operator_add(new RVector(wheelDelta/2, 0));
  219. }
  220. }
  221. if (RSettings.getQtVersionString().startsWith("4.")) {
  222. // TODO: limit check to wheel events if possible:
  223. if (QCoreApplication.hasPendingEvents()) {
  224. return;
  225. }
  226. }
  227. this.view.pan(this.panOffset);
  228. this.panOffset = new RVector();
  229. }
  230. this.view.simulateMouseMoveEvent();
  231. break;
  232. }
  233. };
  234. DefaultNavigation.prototype.tabletEvent = function(event) {
  235. /*
  236. qDebug("-----------------------------");
  237. qDebug("DefaultNavigation.tabletEvent()");
  238. qDebug("type: " + event.type());
  239. qDebug("device: " + event.device());
  240. qDebug("globalX: " + event.globalX());
  241. qDebug("globalY: " + event.globalY());
  242. qDebug("x: " + event.x());
  243. qDebug("y: " + event.y());
  244. qDebug("z: " + event.z());
  245. qDebug("hiResGlobalX: " + event.hiResGlobalX());
  246. qDebug("hiResGlobalY: " + event.hiResGlobalY());
  247. qDebug("pointerType: " + event.pointerType());
  248. qDebug("pointerType: " + event.pointerType());
  249. qDebug("pressure: " + event.pressure());
  250. qDebug("rotation: " + event.rotation());
  251. qDebug("tangentialPressure: " + event.tangentialPressure());
  252. qDebug("uniqueId: " + event.uniqueId());
  253. qDebug("xTilt: " + event.xTilt());
  254. qDebug("yTilt: " + event.yTilt());
  255. */
  256. event.ignore();
  257. };
  258. /**
  259. * Called for pan gestures, e.g. from a track pad.
  260. * Pans the current view.
  261. */
  262. DefaultNavigation.prototype.panGestureEvent = function(gesture) {
  263. if (DefaultNavigation.panGesture===false) {
  264. return;
  265. }
  266. if (isNull(this.view)) {
  267. return;
  268. }
  269. switch (gesture.state) {
  270. case Qt.GestureStarted:
  271. this.lastCursor = this.view.getCursor();
  272. this.view.setCursor(new QCursor(Qt.OpenHandCursor));
  273. break;
  274. case Qt.GestureUpdated:
  275. this.view.setCursor(new QCursor(Qt.ClosedHandCursor));
  276. break;
  277. default:
  278. if (!isNull(this.lastCursor)) {
  279. this.view.setCursor(this.lastCursor);
  280. }
  281. break;
  282. }
  283. var delta = gesture.delta;
  284. this.view.pan(new RVector(delta.x(), delta.y()));
  285. this.view.simulateMouseMoveEvent();
  286. };
  287. /**
  288. * Called for pinch gestures, e.g. from a track pad.
  289. * Zooms in / out.
  290. */
  291. DefaultNavigation.prototype.pinchGestureEvent = function(gesture) {
  292. if (isNull(this.view)) {
  293. return;
  294. }
  295. var changeFlags = gesture.changeFlags;
  296. var center = this.view.getLastKnownMousePosition();
  297. if (!center.isValid()) {
  298. return;
  299. }
  300. if (typeof(QPinchGesture)!=="undefined" && (changeFlags & QPinchGesture.RotationAngleChanged)) {
  301. // rotation does nothing
  302. }
  303. if (typeof(QPinchGesture)!=="undefined" && (changeFlags & QPinchGesture.ScaleFactorChanged)) {
  304. var value;
  305. if (RSettings.getQtVersion() >= 0x050000) {
  306. // Qt 5:
  307. value = gesture.totalScaleFactor;
  308. }
  309. else {
  310. // Qt 4:
  311. value = gesture.property("scaleFactor");
  312. }
  313. if (value<=0.0) {
  314. return;
  315. }
  316. this.view.setCurrentStepScaleFactor(value);
  317. var offset = this.view.getOffset(false);
  318. var newOffset = offset.operator_subtract(
  319. center.operator_multiply(
  320. this.view.getCurrentStepScaleFactor()
  321. ).operator_subtract(center)
  322. );
  323. newOffset = newOffset.operator_divide(this.view.getCurrentStepScaleFactor());
  324. // avoid erratic behavior:
  325. if (offset.getDistanceTo(newOffset) < 10*this.view.mapDistanceFromView(this.view.getWidth())) {
  326. this.view.setCurrentStepOffset(newOffset.operator_subtract(this.view.getOffset(false)));
  327. }
  328. }
  329. if (gesture.state == Qt.GestureFinished) {
  330. this.view.setFactor(this.view.getFactor(true));
  331. this.view.setOffset(this.view.getOffset(true));
  332. this.view.setCurrentStepScaleFactor(1.0);
  333. this.view.setCurrentStepOffset(new RVector(0,0));
  334. }
  335. this.view.simulateMouseMoveEvent();
  336. this.view.regenerate(true);
  337. };