ScMirrored.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  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("../Screencast.js");
  20. /**
  21. * Focus listener. Re-installes event filters if focus has changed
  22. * (e.g. new document).
  23. */
  24. /*
  25. function ScInputInfoFocusListener(ef) {
  26. RFocusListenerAdapter.call(this);
  27. this.eventFilter = ef;
  28. }
  29. ScInputInfoFocusListener.prototype = new RFocusListenerAdapter();
  30. ScInputInfoFocusListener.prototype.updateFocus = function(di) {
  31. var appWin = RMainWindowQt.getMainWindow();
  32. var mdiArea = appWin.findChild("MdiArea");
  33. this.eventFilter.install(mdiArea);
  34. };
  35. */
  36. /**
  37. * Event filter installed for all widgets, tracks mouse, keys, etc.
  38. */
  39. function ScInputInfoEventFilter() {
  40. QObject.call(this);
  41. this.stopped = false;
  42. this.quit = false;
  43. this.buttons = undefined;
  44. this.button = undefined;
  45. this.key = undefined;
  46. this.shortcut = undefined;
  47. this.highlightWidget = undefined;
  48. this.widgetTimer = undefined;
  49. this.mouseInfo = undefined;
  50. this.mouseTimer = undefined;
  51. this.keyInfo = undefined;
  52. this.keyTimer = undefined;
  53. }
  54. ScInputInfoEventFilter.prototype = new QObject();
  55. ScInputInfoEventFilter.prototype.install = function(w) {
  56. if (this.quit) {
  57. return;
  58. }
  59. this.stopped = false;
  60. var kids = w.children();
  61. for (var i=0; i<kids.length; i++) {
  62. var k = kids[i];
  63. if (isNull(k.gotEventFilter)) {
  64. if (isFunction(k.installEventFilter)) {
  65. //k.mouseTracking = true;
  66. k.installEventFilter(this);
  67. k.setProperty("gotEventFilter", true);
  68. }
  69. }
  70. this.install(k);
  71. }
  72. };
  73. ScInputInfoEventFilter.prototype.uninstall = function(w) {
  74. //qDebug("UNINSTALLING EVENT FILTERS");
  75. var kids = w.children();
  76. for (var i=0; i<kids.length; i++) {
  77. var k = kids[i];
  78. if (isNull(k.gotEventFilter) && isFunction(k.removeEventFilter)) {
  79. k.removeEventFilter(this);
  80. k.setProperty("gotEventFilter", undefined);
  81. }
  82. this.uninstall(k);
  83. }
  84. };
  85. ScInputInfoEventFilter.prototype.eventFilter = function(watched, e) {
  86. if (this.stopped || this.quit) {
  87. return false;
  88. }
  89. var appWin = RMainWindowQt.getMainWindow();
  90. e = e.cast();
  91. var type = e.type().toString();
  92. var objectName = watched.objectName;
  93. if (type==="DeferredDelete") {
  94. return true;
  95. }
  96. if (type==="ToolTip") {
  97. return true;
  98. }
  99. //qDebug("type: ", type);
  100. //qDebug("objectName: ", objectName);
  101. if (type.contains("Close") && objectName.contains("Untitled")) {
  102. //qDebug("type: ", type);
  103. //qDebug("objectName: ", objectName);
  104. this.uninstall(appWin);
  105. this.stopped = true;
  106. return false;
  107. }
  108. if (!type.contains("Mouse") &&
  109. !type.contains("Key") &&
  110. !type.contains("Shortcut") &&
  111. !type.contains("Focus") &&
  112. !type.contains("Enter") &&
  113. !type.contains("Leave") &&
  114. !type.contains("ContextMenu")) {
  115. //qDebug("object: ", watched.objectName);
  116. //qDebug("type: ", type);
  117. return false;
  118. }
  119. var installFilter = false;
  120. var updatePos = false;
  121. switch (type) {
  122. case "MouseButtonRelease":
  123. case "MouseButtonPress":
  124. case "MouseMove":
  125. this.button = e.button().valueOf();
  126. this.buttons = e.buttons().valueOf();
  127. break;
  128. }
  129. switch (type) {
  130. case "ContextMenu":
  131. installFilter = true;
  132. break;
  133. case "MouseButtonRelease":
  134. //updatePos = true;
  135. //this.hideMouseLabel();
  136. this.mouseTimer = 500;
  137. break;
  138. case "MouseButtonPress":
  139. //this.showMouseLabel();
  140. if (this.buttons!==Qt.RightButton.valueOf()) {
  141. installFilter = true;
  142. }
  143. this.mouseTimer = -1;
  144. //updatePos = true;
  145. break;
  146. case "MouseMove":
  147. //updatePos = true;
  148. break;
  149. case "Shortcut":
  150. this.shortcut = e.key();
  151. this.keyInfo = this.shortcut.toString();
  152. this.keyTimer = 1500;
  153. qDebug("this.shortcut: ", this.shortcut);
  154. break;
  155. case "KeyPress":
  156. case "ShortcutOverride":
  157. this.key = e.key().valueOf();
  158. this.modifiers = e.modifiers().valueOf();
  159. break;
  160. case "KeyRelease":
  161. this.key = undefined;
  162. this.modifiers = undefined;
  163. break;
  164. }
  165. if (this.button===Qt.LeftButton.valueOf() || this.buttons===Qt.LeftButton.valueOf()) {
  166. this.mouseInfo = "left";
  167. }
  168. else if (this.button===Qt.MidButton.valueOf() || this.buttons===Qt.MidButton.valueOf()) {
  169. this.mouseInfo = "middle";
  170. }
  171. else if (this.button===Qt.RightButton.valueOf() || this.buttons===Qt.RightButton.valueOf()) {
  172. this.mouseInfo = "right";
  173. }
  174. if (this.key===Qt.Key_QuoteLeft.valueOf()) {
  175. this.highlightWidget = QApplication.widgetAt(QCursor.pos());
  176. this.widgetTimer = -1;
  177. //var pos = this.highlightWidget.pos;
  178. //pos.setX(pos.x() + this.highlightWidget.width);
  179. //var pos = new QPoint(this.highlightWidget.width-5, 5);
  180. //var gpos = this.highlightWidget.mapToGlobal(pos);
  181. // TODO: show tool tip at fixed location, away from highlighted widget:
  182. //QToolTip.showText(gpos, this.highlightWidget.toolTip);
  183. // var toolTipEvent = new QHelpEvent(
  184. // QEvent.ToolTip,
  185. // pos,
  186. // QCursor.pos()
  187. // //gpos
  188. // );
  189. // QCoreApplication.postEvent(this.highlightWidget, toolTipEvent);
  190. }
  191. if (this.key===Qt.Key_AsciiTilde.valueOf()) {
  192. this.highlightWidget = QApplication.widgetAt(QCursor.pos()).parentWidget();
  193. this.widgetTimer = -1;
  194. }
  195. if (installFilter) {
  196. if (objectName!=="GraphicsView" &&
  197. objectName!=="MdiArea" &&
  198. objectName!=="Form" &&
  199. !objectName.startsWith("Viewport") &&
  200. objectName!=="Single") {
  201. var self = this;
  202. for (var i=0; i<2; i++) {
  203. var singleShot = new QTimer();
  204. singleShot.singleShot = true;
  205. singleShot.timeout.connect(function() {
  206. //self.mouseLabel.raise();
  207. self.install(appWin);
  208. });
  209. singleShot.start(20 + i*200);
  210. }
  211. }
  212. }
  213. // if (updatePos) {
  214. // var pos = QCursor.pos();
  215. // this.mouseLabel.move(pos.x()+10, pos.y()+10);
  216. // }
  217. return false;
  218. };
  219. /**
  220. * This action creates a Qt main window.
  221. */
  222. function ScMirrored(guiAction) {
  223. EAction.call(this, guiAction);
  224. }
  225. ScMirrored.prototype = new EAction();
  226. ScMirrored.includeBasePath = includeBasePath;
  227. ScMirrored.interval = 20;
  228. ScMirrored.prototype.beginEvent = function() {
  229. EAction.prototype.beginEvent.call(this);
  230. this.titleBarHeight = 22;
  231. this.x = 0;
  232. this.y = 22;
  233. this.w = 1280;
  234. this.targetHeight = 720;
  235. this.h = this.targetHeight - this.y - this.titleBarHeight;
  236. this.iconSize = 32;
  237. var appWin = RMainWindowQt.getMainWindow();
  238. appWin.resize(this.w,this.h);
  239. appWin.move(this.x,this.y);
  240. //appWin.setAttribute(Qt.WA_MacAlwaysShowToolWindow);
  241. //appWin.setAttribute(Qt.WA_MacNoCocoaChildWindow);
  242. //var flags = new Qt.WindowFlags(appWin.windowFlags.valueOf() + Qt.FramelessWindowHint.valueOf());
  243. //var flags = new Qt.WindowFlags(appWin.windowFlags());
  244. //var flags = new Qt.WindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool);
  245. //appWin.setWindowFlags(flags);
  246. var pe = appWin.findChild("PropertyEditorDock");
  247. if (!isNull(pe)) {
  248. pe.show();
  249. pe.maximumWidth = 280;
  250. }
  251. var ll = appWin.findChild("LayerListDock");
  252. if (!isNull(ll)) {
  253. ll.show();
  254. ll.maximumWidth = 220;
  255. }
  256. var bl = appWin.findChild("BlockListDock");
  257. if (!isNull(bl)) {
  258. bl.show();
  259. bl.maximumWidth = 220;
  260. }
  261. var ct = appWin.findChild("CamToolBar");
  262. if (!isNull(ct)) {
  263. ct.hide();
  264. }
  265. var cl = appWin.findChild("CommandLineDock");
  266. if (!isNull(ct)) {
  267. cl.hide();
  268. }
  269. this.mirrorWidget = new QLabel(appWin);
  270. this.mirrorWidget.setAttribute(Qt.WA_MacAlwaysShowToolWindow);
  271. this.mirrorWidget.setAttribute(Qt.WA_TransparentForMouseEvents);
  272. this.mirrorWidget.resize(this.w,this.targetHeight);
  273. this.mirrorWidget.move(this.x+this.w,this.y);
  274. // this.mirrorWidget.resize(this.w,this.h);
  275. // this.mirrorWidget.move(this.x,this.y);
  276. this.mirrorWidget.focusPolicy = Qt.NoFocus;
  277. var flags = makeQtWindowFlags(Qt.FramelessWindowHint, Qt.WindowStaysOnTopHint, Qt.Tool);
  278. this.mirrorWidget.setWindowFlags(flags);
  279. this.mirrorWidget.setAttribute(Qt.WA_MacNoShadow);
  280. this.mirrorWidget.setAttribute(Qt.WA_DeleteOnClose);
  281. this.mirrorWidget.setAttribute(Qt.WA_ShowWithoutActivating);
  282. this.mirrorWidget.show();
  283. this.cursors = {
  284. "ArrowCursor": [5,5],
  285. "CrossCursor": [15,15],
  286. "WindowZoomCursor": [12,12],
  287. "PrintPreviewOffsetCursor": [16,16],
  288. "OpenHandCursor": [16,16],
  289. "ClosedHandCursor": [16,16],
  290. "IBeamCursor": [16,16]
  291. };
  292. for (var cursorShape in this.cursors) {
  293. this.cursors[cursorShape].push(ScMirrored.createPixmap(
  294. ScMirrored.includeBasePath + "/" + cursorShape + ".svg", this.iconSize,this.iconSize));
  295. }
  296. this.mousePixmap = [];
  297. this.mousePixmap["left"] = ScMirrored.createPixmap(ScMirrored.includeBasePath + "/MouseButtonLeft.svg", this.iconSize,this.iconSize);
  298. this.mousePixmap["middle"] = ScMirrored.createPixmap(ScMirrored.includeBasePath + "/MouseButtonMiddle.svg", this.iconSize,this.iconSize);
  299. this.mousePixmap["right"] = ScMirrored.createPixmap(ScMirrored.includeBasePath + "/MouseButtonRight.svg", this.iconSize,this.iconSize);
  300. this.keyPixmap = ScMirrored.createPixmap(ScMirrored.includeBasePath + "/Key.svg", this.iconSize,this.iconSize);
  301. this.keyWidePixmap = ScMirrored.createPixmap(ScMirrored.includeBasePath + "/KeyWide.svg", this.iconSize*1.5,this.iconSize);
  302. // start mirroring:
  303. var t = new QTimer(this.mirrorWidget);
  304. t.interval = ScMirrored.interval;
  305. t.timeout.connect(this, "mirror");
  306. this.ef = new ScInputInfoEventFilter(this);
  307. this.ef.install(appWin);
  308. var self = this;
  309. appWin.closeRequested.connect(function() {
  310. qDebug("ScMirrored: closeRequested");
  311. self.ef.stopped = true;
  312. self.ef.quit = true;
  313. //self.ef.uninstall(appWin.findChild("MdiArea"));
  314. self.ef.uninstall(appWin);
  315. qDebug("ScMirrored: closeRequested: OK");
  316. });
  317. //var fl = new ScInputInfoFocusListener(ef);
  318. //appWin.addFocusListener(fl);
  319. //appWin.setProperty("ScreencastRunning", true);
  320. t.start();
  321. this.terminate();
  322. };
  323. ScMirrored.prototype.mirror = function() {
  324. var pm = QPixmap.grabWindow(QApplication.desktop().winId(), 0, 0, this.w, this.targetHeight);
  325. var painter = new QPainter();
  326. painter.begin(pm);
  327. //qDebug("bs: ", this.ef.buttons);
  328. //qDebug("b: ", this.ef.button);
  329. //if (!isNull(this.ef.key)) {
  330. //qDebug("k: ", "0x%1".arg(this.ef.key.valueOf(), 0, 16));
  331. //}
  332. //qDebug("m: ", this.ef.modifiers);
  333. //qDebug("shortcut: ", this.ef.shortcut);
  334. this.paintLogo(painter, pm);
  335. this.paintHighlightedWidget(painter);
  336. this.paintCursor(painter);
  337. this.paintMouseInfo(painter);
  338. this.paintKeyInfo(painter);
  339. painter.end();
  340. this.mirrorWidget.pixmap = pm;
  341. collectGarbage();
  342. };
  343. /**
  344. * Overpaint a**le logo.
  345. */
  346. ScMirrored.prototype.paintLogo = function(painter, pixmap) {
  347. if (RS.getSystemId()!=="osx") {
  348. return;
  349. }
  350. var bg = pixmap.copy(0,0,18,21);
  351. painter.drawPixmap(19,0,bg);
  352. //bg = pixmap.copy(0,44,1280,714);
  353. //painter.drawPixmap(0,21,bg);
  354. //bg = pixmap.copy(70,25,60,18);
  355. //painter.drawPixmap(5,25,bg);
  356. };
  357. /**
  358. * Paint mouse cursor.
  359. */
  360. ScMirrored.prototype.paintCursor = function(painter) {
  361. var pos = QCursor.pos();
  362. var widget = QApplication.widgetAt(pos);
  363. var shape;
  364. if (!isNull(widget)) {
  365. shape = widget.cursor.shape().toString();
  366. }
  367. if (shape==="BitmapCursor") {
  368. shape = widget["CursorName"];
  369. }
  370. if (isNull(shape)|| isNull(this.cursors[shape])) {
  371. shape = "ArrowCursor";
  372. }
  373. var cursor = this.cursors[shape];
  374. pos.operator_add_assign(new QPoint(-cursor[0], -cursor[1]));
  375. painter.drawPixmap(pos, cursor[2]);
  376. };
  377. /**
  378. * Paint mouse button info.
  379. */
  380. ScMirrored.prototype.paintMouseInfo = function(painter) {
  381. if (this.ef.mouseTimer<=0 && this.ef.mouseTimer!==-1) {
  382. return;
  383. }
  384. var pos = QCursor.pos();
  385. pos.setX(pos.x() + this.iconSize/2);
  386. pos.setY(pos.y() + this.iconSize);
  387. var alpha;
  388. if (this.ef.mouseTimer!==-1) {
  389. this.ef.mouseTimer = Math.max(this.ef.mouseTimer-ScMirrored.interval, 0);
  390. alpha = Math.max(Math.min(this.ef.mouseTimer, 255), 0);
  391. }
  392. else {
  393. alpha = 255;
  394. }
  395. painter.setOpacity(alpha/255);
  396. if (!isNull(this.mousePixmap[this.ef.mouseInfo])) {
  397. painter.drawPixmap(pos, this.mousePixmap[this.ef.mouseInfo]);
  398. }
  399. };
  400. /**
  401. * Paint key info.
  402. */
  403. ScMirrored.prototype.paintKeyInfo = function(painter) {
  404. //qDebug("keyInfo: ", this.ef.keyInfo);
  405. //qDebug("keyTimer: ", this.ef.keyTimer);
  406. if (this.ef.keyTimer<=0 && this.ef.keyTimer!==-1) {
  407. return;
  408. }
  409. if (isNull(this.ef.keyInfo)) {
  410. return;
  411. }
  412. var pos = QCursor.pos();
  413. pos.setX(pos.x() + this.iconSize*1.5);
  414. pos.setY(pos.y() + this.iconSize);
  415. // var posText = pos;
  416. // posText.setX(pos.x() + this.iconSize*1.5 + this.iconSize*0.5);
  417. // posText.setY(pos.y() + this.iconSize*0.5);
  418. var alpha;
  419. if (this.ef.keyTimer!==-1) {
  420. this.ef.keyTimer = Math.max(this.ef.keyTimer-ScMirrored.interval, 0);
  421. alpha = Math.max(Math.min(this.ef.keyTimer, 255), 0);
  422. }
  423. else {
  424. alpha = 255;
  425. }
  426. painter.setOpacity(alpha/255);
  427. var keys = this.ef.keyInfo.split(/[,+]/);
  428. var x = pos.x();
  429. for (var i=0; i<keys.length; i++) {
  430. var keyText = keys[i].trim();
  431. var f = 1.0;
  432. var pm = this.keyPixmap;
  433. if (keyText.length>1) {
  434. f = 1.5;
  435. pm = this.keyWidePixmap;
  436. }
  437. painter.drawPixmap(x, pos.y(), pm);
  438. painter.drawText(x, pos.y(), this.iconSize*f, this.iconSize, Qt.AlignCenter.valueOf(), keyText, new QRectF());
  439. x += this.iconSize*f*1.2;
  440. }
  441. };
  442. /**
  443. * Highlight widget under cursor.
  444. */
  445. ScMirrored.prototype.paintHighlightedWidget = function(painter) {
  446. if (isNull(this.ef.highlightWidget)) {
  447. return;
  448. }
  449. if (this.ef.widgetTimer<=0 && this.ef.widgetTimer!==-1) {
  450. return;
  451. }
  452. var pos = QCursor.pos();
  453. var widget = QApplication.widgetAt(pos);
  454. var parentWidget = undefined;
  455. if (!isNull(widget)) {
  456. parentWidget = widget.parentWidget();
  457. }
  458. // cursor moved away: fade out:
  459. if (widget!==this.ef.highlightWidget && parentWidget!==this.ef.highlightWidget &&
  460. (this.ef.widgetTimer>500 || this.ef.widgetTimer===-1)) {
  461. this.ef.widgetTimer = 500;
  462. }
  463. var alpha;
  464. if (this.ef.widgetTimer!==-1) {
  465. this.ef.widgetTimer = Math.max(this.ef.widgetTimer-ScMirrored.interval, 0);
  466. alpha = Math.max(Math.min(this.ef.widgetTimer, 255), 0);
  467. }
  468. else {
  469. alpha = 255;
  470. }
  471. //qDebug("widget.objectName: ", widget.objectName);
  472. painter.setBrush(new QBrush(new QColor(255,0,0,alpha/8)));
  473. var pen = new QPen(new QColor(255,0,0,alpha/2));
  474. pen.setWidth(2);
  475. painter.setPen(pen);
  476. var wpos = this.ef.highlightWidget.mapToGlobal(new QPoint(0,0));
  477. painter.drawRect(wpos.x(), wpos.y(), this.ef.highlightWidget.width, this.ef.highlightWidget.height);
  478. };
  479. ScMirrored.createPixmap = function(file, w, h) {
  480. var pixmap = new QPixmap(w,h);
  481. pixmap.fill(new QColor(0,0,0,0));
  482. var p = new QPainter();
  483. p.begin(pixmap);
  484. //var r = new QSvgRenderer();
  485. //r.load(file);
  486. //r.render(p);
  487. p.end();
  488. return pixmap;
  489. }