Coverage for paintmaskanimator/main_window_input.py: 68%

390 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-11-02 04:50 +0000

1"""Application-level keyboard/mouse input handling for MainWindow. 

2 

3Split out of ``main_window.py`` as a mixin. This is the ``eventFilter`` installed 

4on the application and the machinery behind it: shortcut-token normalisation and 

5matching, the auxiliary hold-to-drag gestures (hand scroll, rotate, zoom) with 

6their cursor bookkeeping, and the canvas hold-operation sync. Keeping it separate 

7means the window shell no longer carries the event-loop hot path. 

8""" 

9import time 

10from typing import Any 

11 

12from PySide6.QtCore import QEvent, QObject, QPoint, QPointF, QTimer, Qt 

13from PySide6.QtGui import QCursor, QKeySequence 

14from PySide6.QtWidgets import QAbstractButton, QApplication, QSplitter, QTabBar, QWidget 

15from ._main_window_members import MainWindowMembers 

16from .constants import HOLD_ZOOM_SHORTCUT 

17from .utils import drag_zoom_factor, magnifier_cursor 

18from .widgets import ShortcutDialog 

19from .errors import OPERATION_ERRORS 

20from .logging_setup import get_logger 

21 

22log = get_logger(__name__) 

23 

24_OPERATION_ERRORS = OPERATION_ERRORS 

25 

26 

27class InputMixin(MainWindowMembers): 

28 def _is_color_chart_widget(self, watched): 

29 """Return safely during Qt teardown when the chart is already gone.""" 

30 if not isinstance(watched, QWidget): 

31 return False 

32 chart = getattr(self, "color_chart", None) 

33 if chart is None: 33 ↛ 34line 33 didn't jump to line 34 because the condition on line 33 was never true

34 return False 

35 try: 

36 return watched is chart or chart.isAncestorOf(watched) 

37 except RuntimeError: 

38 # Qt can deliver one final application event after ADS has deleted 

39 # the dock contents but before this Python event filter is removed. 

40 return False 

41 

42 @staticmethod 

43 def _normalize_shortcut_token(token): 

44 aliases = { 

45 "Control": "Ctrl", 

46 "CTRL": "Ctrl", 

47 "SHIFT": "Shift", 

48 "ALT": "Alt", 

49 "META": "Meta", 

50 " ": "Space", 

51 } 

52 token = str(token).strip() 

53 return aliases.get(token, token) 

54 

55 def _shortcut_tokens(self, action, fallback): 

56 stored = action.property("holdShortcutText") 

57 if stored is not None: 

58 text = str(stored) 

59 else: 

60 text = action.shortcut().toString( 

61 QKeySequence.SequenceFormat.PortableText 

62 ) 

63 if not text: 63 ↛ 64line 63 didn't jump to line 64 because the condition on line 63 was never true

64 text = fallback 

65 # Hold operations use the first chord only. 

66 text = text.split(",", 1)[0] 

67 return { 

68 self._normalize_shortcut_token(token) 

69 for token in text.split("+") 

70 if token.strip() 

71 } 

72 

73 def _event_key_token(self, event): 

74 key_map = { 

75 Qt.Key.Key_Control: "Ctrl", 

76 Qt.Key.Key_Shift: "Shift", 

77 Qt.Key.Key_Alt: "Alt", 

78 Qt.Key.Key_Meta: "Meta", 

79 Qt.Key.Key_Space: "Space", 

80 } 

81 if event.key() in key_map: 

82 return key_map[event.key()] 

83 text = QKeySequence(int(event.key())).toString( 

84 QKeySequence.SequenceFormat.PortableText 

85 ) 

86 return self._normalize_shortcut_token(text) if text else None 

87 

88 def _sync_modifier_tokens(self, modifiers): 

89 mapping = ( 

90 (Qt.KeyboardModifier.ControlModifier, "Ctrl"), 

91 (Qt.KeyboardModifier.ShiftModifier, "Shift"), 

92 (Qt.KeyboardModifier.AltModifier, "Alt"), 

93 (Qt.KeyboardModifier.MetaModifier, "Meta"), 

94 ) 

95 for flag, token in mapping: 

96 if modifiers & flag: 

97 self._held_canvas_shortcut_tokens.add(token) 

98 else: 

99 self._held_canvas_shortcut_tokens.discard(token) 

100 

101 def _widget_in_timeline(self, widget): 

102 return bool( 

103 isinstance(widget, QWidget) 

104 and ( 

105 widget is self.timeline 

106 or self.timeline.isAncestorOf(widget) 

107 ) 

108 ) 

109 

110 def _hand_scroll_area_for_widget(self, widget): 

111 if not isinstance(widget, QWidget): 111 ↛ 112line 111 didn't jump to line 112 because the condition on line 111 was never true

112 return None 

113 if self._widget_in_timeline(widget): 

114 return self.timeline.table 

115 candidates = ( 

116 self.palette.scroll, 

117 self.tools_scroll, 

118 self.color_wheel_scroll, 

119 self.color_slider_scroll, 

120 self.palette_scroll, 

121 ) 

122 for area in candidates: 

123 if widget is area or area.isAncestorOf(widget): 123 ↛ 124line 123 didn't jump to line 124 because the condition on line 123 was never true

124 return area 

125 return None 

126 

127 @staticmethod 

128 def _mouse_global_position(event): 

129 if hasattr(event, "globalPosition"): 129 ↛ 131line 129 didn't jump to line 131 because the condition on line 129 was always true

130 return QPointF(event.globalPosition()) 

131 return QPointF(QCursor.pos()) 

132 

133 def _auxiliary_cursor_targets(self): 

134 return ( 

135 self.timeline, 

136 self.timeline.table.viewport(), 

137 self.tools_dock, 

138 self.tools_scroll.viewport(), 

139 self.color_wheel_dock, 

140 self.color_wheel_scroll.viewport(), 

141 self.color_slider_dock, 

142 self.color_slider_scroll.viewport(), 

143 self.palette_dock, 

144 self.palette_scroll.viewport(), 

145 self.palette.scroll.viewport(), 

146 ) 

147 

148 def _update_auxiliary_hold_cursors(self): 

149 tool = self.canvas.temp_tool 

150 for widget in self._auxiliary_cursor_targets(): 

151 try: 

152 widget.unsetCursor() 

153 except RuntimeError: 

154 log.debug("cursor target was deleted during cleanup", exc_info=True) 

155 if tool == "hand": 

156 for widget in self._auxiliary_cursor_targets(): 

157 try: 

158 widget.setCursor(Qt.CursorShape.OpenHandCursor) 

159 except RuntimeError: 

160 log.debug("cursor target was deleted during update", exc_info=True) 

161 elif tool == "zoom": 161 ↛ 162line 161 didn't jump to line 162 because the condition on line 161 was never true

162 try: 

163 self.timeline.setCursor(magnifier_cursor()) 

164 self.timeline.table.viewport().setCursor(magnifier_cursor()) 

165 except RuntimeError: 

166 log.debug("timeline was deleted during cursor update", exc_info=True) 

167 

168 def _finish_auxiliary_hold_drag(self): 

169 grab_widget = self._ui_hold_grab_widget 

170 self._ui_hold_drag_mode = None 

171 self._ui_hold_scroll_area = None 

172 self._ui_hold_grab_widget = None 

173 if grab_widget is not None: 

174 try: 

175 grab_widget.releaseMouse() 

176 except RuntimeError: 

177 log.debug("mouse grab owner was deleted during release", exc_info=True) 

178 self._update_auxiliary_hold_cursors() 

179 

180 def _handle_auxiliary_hold_event(self, watched, event): 

181 event_type = event.type() 

182 tool = self.canvas.temp_tool 

183 

184 if self._ui_hold_drag_mode is not None: 184 ↛ 185line 184 didn't jump to line 185 because the condition on line 184 was never true

185 if event_type == QEvent.Type.MouseMove: 

186 current = self._mouse_global_position(event) 

187 if self._ui_hold_drag_mode == "hand": 

188 area = self._ui_hold_scroll_area 

189 if area is not None: 

190 delta = current - self._ui_hold_start_global 

191 horizontal, vertical = self._ui_hold_start_scroll 

192 area.horizontalScrollBar().setValue( 

193 int(round(horizontal - delta.x())) 

194 ) 

195 area.verticalScrollBar().setValue( 

196 int(round(vertical - delta.y())) 

197 ) 

198 area.viewport().setCursor( 

199 Qt.CursorShape.ClosedHandCursor 

200 ) 

201 elif self._ui_hold_drag_mode == "timeline_zoom": 

202 delta_x = current.x() - self._ui_hold_last_global.x() 

203 factor = drag_zoom_factor(delta_x) 

204 viewport = self.timeline.table.viewport() 

205 anchor = viewport.mapFromGlobal( 

206 current.toPoint() 

207 ) 

208 self.timeline.adjust_timeline_zoom( 

209 factor, 

210 anchor.x(), 

211 anchor.y(), 

212 ) 

213 self._ui_hold_last_global = current 

214 event.accept() 

215 return True 

216 if event_type == QEvent.Type.MouseButtonRelease: 

217 self._finish_auxiliary_hold_drag() 

218 event.accept() 

219 return True 

220 

221 # ズームツール保持中、またはAltを押しながらのホイールでタイムラインを拡大縮小する。 

222 if event_type == QEvent.Type.Wheel and ( 

223 tool == "zoom" 

224 or event.modifiers() & Qt.KeyboardModifier.AltModifier 

225 ): 

226 if self._widget_in_timeline(watched): 226 ↛ 242line 226 didn't jump to line 242 because the condition on line 226 was always true

227 angle = event.angleDelta() 

228 # Alt+ホイールを横スクロールに読み替える環境では、縦の量が横に入る。 

229 delta = angle.y() or angle.x() 

230 if delta: 230 ↛ 242line 230 didn't jump to line 242 because the condition on line 230 was always true

231 viewport = self.timeline.table.viewport() 

232 global_point = self._mouse_global_position(event) 

233 anchor = viewport.mapFromGlobal(global_point.toPoint()) 

234 self.timeline.adjust_timeline_zoom( 

235 1.15 ** (delta / 120), 

236 anchor.x(), 

237 anchor.y(), 

238 ) 

239 event.accept() 

240 return True 

241 

242 if event_type == QEvent.Type.MouseButtonPress: 

243 if event.button() != Qt.MouseButton.LeftButton: 243 ↛ 244line 243 didn't jump to line 244 because the condition on line 243 was never true

244 return False 

245 area = self._hand_scroll_area_for_widget(watched) 

246 if tool == "hand" and area is not None: 246 ↛ 247line 246 didn't jump to line 247 because the condition on line 246 was never true

247 self._ui_hold_drag_mode = "hand" 

248 self._ui_hold_scroll_area = area 

249 self._ui_hold_start_global = self._mouse_global_position(event) 

250 self._ui_hold_last_global = QPointF( 

251 self._ui_hold_start_global 

252 ) 

253 self._ui_hold_start_scroll = ( 

254 area.horizontalScrollBar().value(), 

255 area.verticalScrollBar().value(), 

256 ) 

257 self._ui_hold_grab_widget = ( 

258 watched if isinstance(watched, QWidget) else area.viewport() 

259 ) 

260 try: 

261 self._ui_hold_grab_widget.grabMouse() 

262 except RuntimeError: 

263 self._ui_hold_grab_widget = None 

264 area.viewport().setCursor(Qt.CursorShape.ClosedHandCursor) 

265 event.accept() 

266 return True 

267 if tool == "zoom" and self._widget_in_timeline(watched): 267 ↛ 268line 267 didn't jump to line 268 because the condition on line 267 was never true

268 current = self._mouse_global_position(event) 

269 self._ui_hold_drag_mode = "timeline_zoom" 

270 self._ui_hold_scroll_area = self.timeline.table 

271 self._ui_hold_start_global = current 

272 self._ui_hold_last_global = QPointF(current) 

273 self._ui_hold_grab_widget = ( 

274 watched 

275 if isinstance(watched, QWidget) 

276 else self.timeline.table.viewport() 

277 ) 

278 try: 

279 self._ui_hold_grab_widget.grabMouse() 

280 except RuntimeError: 

281 self._ui_hold_grab_widget = None 

282 event.accept() 

283 return True 

284 

285 if event_type == QEvent.Type.MouseMove: 

286 if tool == "hand" and self._hand_scroll_area_for_widget(watched): 286 ↛ 287line 286 didn't jump to line 287 because the condition on line 286 was never true

287 event.accept() 

288 return True 

289 if tool == "zoom" and self._widget_in_timeline(watched): 289 ↛ 290line 289 didn't jump to line 290 because the condition on line 289 was never true

290 event.accept() 

291 return True 

292 return False 

293 

294 def _update_canvas_hold_operation(self): 

295 held = set(self._held_canvas_shortcut_tokens) 

296 bindings = [ 

297 ("zoom", self.a_hold_zoom, HOLD_ZOOM_SHORTCUT), 

298 ("rotate", self.a_hold_rotate, "Shift+Space"), 

299 ("hand", self.a_hold_hand, "Space"), 

300 ("eyedropper", self.a_hold_eyedropper, "Alt"), 

301 ] 

302 matches = [] 

303 for priority, (tool, action, fallback) in enumerate(bindings): 

304 required = self._shortcut_tokens(action, fallback) 

305 if required and required.issubset(held): 

306 matches.append((len(required), -priority, tool)) 

307 new_tool = max(matches)[2] if matches else None 

308 if {"Ctrl", "Alt"}.issubset(held) and self.canvas.tool in ("brush", "line"): 

309 new_tool = None 

310 if self.canvas.temp_tool != new_tool: 

311 self.canvas.temp_tool = new_tool 

312 self.canvas.drawing = False 

313 self.canvas.middle_hand = False 

314 self.canvas.update_tool_cursor() 

315 self.canvas.update() 

316 if ( 316 ↛ 323line 316 didn't jump to line 323 because the condition on line 316 was never true

317 self._ui_hold_drag_mode == "hand" 

318 and new_tool != "hand" 

319 ) or ( 

320 self._ui_hold_drag_mode == "timeline_zoom" 

321 and new_tool != "zoom" 

322 ): 

323 self._finish_auxiliary_hold_drag() 

324 else: 

325 self._update_auxiliary_hold_cursors() 

326 

327 #: Space をこの秒数より短く押して離したら、ハンドではなく再生/停止にする。 

328 SPACE_TAP_SECONDS = 0.3 

329 

330 def _space_tap_interrupted(self): 

331 """Space を押している間に別の操作をしたら、短押し(再生)とみなさない。""" 

332 self._space_tap_started = None 

333 

334 def _handle_space_tap(self, token, pressed): 

335 """Space の短押しを再生/停止に、長押しをハンド(画面の移動)にする。 

336 

337 ハンドは押した瞬間から効くので、押してすぐドラッグすれば画面が動く。 

338 ドラッグもせず他のキーも押さずに短く離したときだけ再生/停止する。 

339 """ 

340 if pressed: 

341 if token == "Space": 

342 others = self._held_canvas_shortcut_tokens - {"Space"} 

343 # Shift+Space(回転)などの組み合わせは短押しにしない。 

344 self._space_tap_started = None if others else time.monotonic() 

345 else: 

346 self._space_tap_interrupted() 

347 return 

348 if token != "Space": 

349 return 

350 started = self._space_tap_started 

351 self._space_tap_started = None 

352 if started is None: 

353 return 

354 if time.monotonic() - started < self.SPACE_TAP_SECONDS: 

355 self.a_tl_play.trigger() 

356 

357 def eventFilter(self, watched, event): 

358 if not isinstance(watched, QObject): 358 ↛ 361line 358 didn't jump to line 361 because the condition on line 358 was never true

359 # QObject ではない watched を super().eventFilter に渡すと TypeError に 

360 # なる。理由は SubView.eventFilter のコメントを参照。 

361 return False 

362 if ( 

363 self._space_tap_started is not None 

364 and event.type() in (QEvent.Type.MouseButtonPress, QEvent.Type.Wheel) 

365 ): 

366 self._space_tap_interrupted() 

367 if isinstance(watched, QWidget) and event.type() in ( 

368 QEvent.Type.ContextMenu, 

369 QEvent.Type.MouseButtonPress, 

370 QEvent.Type.MouseButtonRelease, 

371 ): 

372 candidate = watched 

373 on_dock_chrome = False 

374 while candidate is not None: 

375 if ( 

376 isinstance(candidate, QTabBar) 

377 or type(candidate).__name__ == "CDockWidgetTab" 

378 or type(candidate).__name__ == "CDockAreaTitleBar" 

379 ): 

380 on_dock_chrome = True 

381 break 

382 candidate = candidate.parentWidget() 

383 if on_dock_chrome and ( 383 ↛ 387line 383 didn't jump to line 387 because the condition on line 383 was never true

384 event.type() == QEvent.Type.ContextMenu 

385 or event.button() == Qt.MouseButton.RightButton 

386 ): 

387 event.accept() 

388 return True 

389 

390 pending_tab_drag = getattr(self, "_pending_dock_tab_drag", None) 

391 if pending_tab_drag is not None: 

392 if event.type() == QEvent.Type.MouseMove and ( 392 ↛ 417line 392 didn't jump to line 417 because the condition on line 392 was always true

393 event.buttons() & Qt.MouseButton.LeftButton 

394 ): 

395 dock, press_global, press_in_tab = pending_tab_drag 

396 current_global = event.globalPosition().toPoint() 

397 if (current_global - press_global).manhattanLength() >= 4: 397 ↛ 422line 397 didn't jump to line 422 because the condition on line 397 was always true

398 self._pending_dock_tab_drag = None 

399 dock.setFloating() 

400 floating = dock.window() 

401 self._floating_move_window = floating 

402 self._floating_tab_drag_dock = dock 

403 floating_tab = dock.tabWidget() 

404 grab_point = QPoint( 

405 max(0, min(press_in_tab.x(), floating_tab.width() - 1)), 

406 max(0, min(press_in_tab.y(), floating_tab.height() - 1)), 

407 ) 

408 self._floating_move_offset = ( 

409 floating_tab.mapToGlobal(grab_point) 

410 - floating.frameGeometry().topLeft() 

411 ) 

412 self._start_manual_tab_drag_polling() 

413 floating.move(current_global - self._floating_move_offset) 

414 self._update_manual_tab_drop(current_global) 

415 event.accept() 

416 return True 

417 elif event.type() == QEvent.Type.MouseButtonRelease: 

418 self._pending_dock_tab_drag = None 

419 event.accept() 

420 return True 

421 

422 moving_window = getattr(self, "_floating_move_window", None) 

423 if moving_window is not None: 

424 if event.type() == QEvent.Type.MouseMove and ( 

425 event.buttons() & Qt.MouseButton.LeftButton 

426 ): 

427 offset = getattr(self, "_floating_move_offset", QPoint()) 

428 moving_window.move(event.globalPosition().toPoint() - offset) 

429 if getattr(self, "_floating_tab_drag_dock", None) is not None: 429 ↛ 433line 429 didn't jump to line 433 because the condition on line 429 was always true

430 self._update_manual_tab_drop( 

431 event.globalPosition().toPoint() 

432 ) 

433 event.accept() 

434 return True 

435 if event.type() == QEvent.Type.MouseButtonRelease: 

436 self._manual_tab_drag_timer.stop() 

437 if getattr(self, "_floating_tab_drag_dock", None) is not None: 437 ↛ 441line 437 didn't jump to line 441 because the condition on line 437 was always true

438 self._finish_manual_tab_drop( 

439 event.globalPosition().toPoint() 

440 ) 

441 self._floating_drag_grabber = None 

442 self._floating_move_window = None 

443 self._floating_tab_drag_dock = None 

444 event.accept() 

445 return True 

446 

447 if ( 

448 event.type() == QEvent.Type.MouseButtonPress 

449 and event.button() == Qt.MouseButton.LeftButton 

450 and isinstance(watched, QWidget) 

451 ): 

452 candidate = watched 

453 # ADS の CFloatingDockContainer / CDockWidgetTab は型名で見分けるので Any。 

454 floating: Any = None 

455 blocked_by_button = False 

456 tab_drag = False 

457 dock_tab: Any = None 

458 on_dock_title_bar = False 

459 while candidate is not None: 

460 if isinstance(candidate, QAbstractButton): 460 ↛ 461line 460 didn't jump to line 461 because the condition on line 460 was never true

461 blocked_by_button = True 

462 break 

463 if isinstance(candidate, QTabBar): 463 ↛ 464line 463 didn't jump to line 464 because the condition on line 463 was never true

464 tab_drag = True 

465 if type(candidate).__name__ == "CDockWidgetTab": 

466 tab_drag = True 

467 dock_tab = candidate 

468 if type(candidate).__name__ == "CDockAreaTitleBar": 

469 on_dock_title_bar = True 

470 floating = getattr( 

471 candidate, "_paintmask_move_container", None 

472 ) 

473 if floating is not None: 473 ↛ 474line 473 didn't jump to line 474 because the condition on line 473 was never true

474 break 

475 candidate = candidate.parentWidget() 

476 if floating is None and on_dock_title_bar: 

477 current_window = watched.window() 

478 if type(current_window).__name__ == "CFloatingDockContainer": 478 ↛ 479line 478 didn't jump to line 479 because the condition on line 478 was never true

479 floating = current_window 

480 if ( 

481 floating is None 

482 and dock_tab is not None 

483 and not blocked_by_button 

484 ): 

485 dock = dock_tab.dockWidget() 

486 area = dock.dockAreaWidget() 

487 if area is not None: 487 ↛ 489line 487 didn't jump to line 489 because the condition on line 487 was always true

488 area.setCurrentDockWidget(dock) 

489 self._pending_dock_tab_drag = ( 

490 dock, 

491 event.globalPosition().toPoint(), 

492 dock_tab.mapFromGlobal(event.globalPosition().toPoint()), 

493 ) 

494 event.accept() 

495 return True 

496 if floating is not None and not blocked_by_button: 496 ↛ 497line 496 didn't jump to line 497 because the condition on line 496 was never true

497 if tab_drag: 

498 self._floating_move_window = floating 

499 self._floating_tab_drag_dock = ( 

500 floating.topLevelDockWidget() 

501 ) 

502 self._start_manual_tab_drag_polling() 

503 self._floating_move_offset = ( 

504 event.globalPosition().toPoint() 

505 - floating.frameGeometry().topLeft() 

506 ) 

507 event.accept() 

508 return True 

509 # CFloatingDockContainer can expose a QWidgetWindow handle 

510 # which is not an OS top-level window. startSystemMove() then 

511 # emits "must be a top level window" on a plain click. Manual 

512 # movement below works for both native and alien containers. 

513 self._floating_move_window = floating 

514 self._floating_move_offset = ( 

515 event.globalPosition().toPoint() 

516 - floating.frameGeometry().topLeft() 

517 ) 

518 event.accept() 

519 return True 

520 

521 # カラーチャート上では、フォーカスがボタンやスクロールバーに 

522 # あってもSpace系操作をチャートキャンバスへ渡す。 

523 if ( 523 ↛ 531line 523 didn't jump to line 531 because the condition on line 523 was never true

524 self._is_color_chart_widget(watched) 

525 and event.type() in ( 

526 QEvent.Type.ShortcutOverride, 

527 QEvent.Type.KeyPress, 

528 QEvent.Type.KeyRelease, 

529 ) 

530 ): 

531 if ( 

532 event.type() == QEvent.Type.ShortcutOverride 

533 and event.key() in (Qt.Key.Key_Space, Qt.Key.Key_Control) 

534 ): 

535 event.accept() 

536 return True 

537 if ( 

538 event.type() in (QEvent.Type.KeyPress, QEvent.Type.KeyRelease) 

539 and event.key() in (Qt.Key.Key_Space, Qt.Key.Key_Control) 

540 and not event.isAutoRepeat() 

541 ): 

542 self.color_chart.tile_canvas.handle_hold_key_event(event) 

543 event.accept() 

544 return True 

545 

546 # サブビューパネルのSpace系操作を、アプリ全体のキャンバス用 

547 # ショートカットへ横取りさせない。 

548 if ( 548 ↛ 564line 548 didn't jump to line 564 because the condition on line 548 was never true

549 isinstance(watched, QWidget) 

550 and ( 

551 watched is self.subview 

552 or self.subview.isAncestorOf(watched) 

553 ) 

554 and event.type() in ( 

555 QEvent.Type.ShortcutOverride, 

556 QEvent.Type.KeyPress, 

557 QEvent.Type.KeyRelease, 

558 QEvent.Type.MouseButtonPress, 

559 QEvent.Type.MouseButtonRelease, 

560 QEvent.Type.MouseMove, 

561 QEvent.Type.Wheel, 

562 ) 

563 ): 

564 if ( 

565 event.type() == QEvent.Type.ShortcutOverride 

566 and event.key() in (Qt.Key.Key_Space, Qt.Key.Key_Control) 

567 ): 

568 event.accept() 

569 return True 

570 if ( 

571 event.type() in (QEvent.Type.KeyPress, QEvent.Type.KeyRelease) 

572 and event.key() in (Qt.Key.Key_Space, Qt.Key.Key_Control) 

573 and not event.isAutoRepeat() 

574 ): 

575 self.subview.handle_hold_key_event(event) 

576 event.accept() 

577 return True 

578 return super().eventFilter(watched, event) 

579 if event.type() == QEvent.Type.MouseButtonRelease: 

580 QTimer.singleShot(0, self._sync_all_area_hamburgers) 

581 if isinstance(watched, QWidget) and type(watched).__name__ == "QSplitterHandle": 

582 splitter = watched.parentWidget() 

583 area = self.tool_selector_dock.dockAreaWidget() 

584 if ( 

585 isinstance(splitter, QSplitter) 

586 and splitter.orientation() == Qt.Orientation.Horizontal 

587 and area is not None 

588 and splitter.indexOf(area) >= 0 

589 ): 

590 if event.type() == QEvent.Type.MouseButtonPress: 

591 self._tool_selector_resize_drag_active = True 

592 elif event.type() == QEvent.Type.MouseButtonRelease: 

593 self._tool_selector_resize_drag_active = False 

594 if ( 

595 event.type() == QEvent.Type.MouseButtonRelease 

596 and self._pending_tool_selector_snap is not None 

597 ): 

598 self._tool_selector_snap_timer.stop() 

599 QTimer.singleShot(0, self._apply_tool_selector_snap) 

600 if event.type() in ( 

601 QEvent.Type.MouseButtonPress, 

602 QEvent.Type.MouseButtonRelease, 

603 QEvent.Type.MouseMove, 

604 QEvent.Type.Wheel, 

605 ) and self._handle_auxiliary_hold_event(watched, event): 

606 return True 

607 if ( 607 ↛ 612line 607 didn't jump to line 612 because the condition on line 607 was never true

608 event.type() == QEvent.Type.KeyPress 

609 and self.canvas.transform_active 

610 and event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter) 

611 ): 

612 self.canvas.commit_selection_transform() 

613 return True 

614 if ( 614 ↛ 619line 614 didn't jump to line 619 because the condition on line 614 was never true

615 event.type() == QEvent.Type.KeyPress 

616 and self.canvas.transform_active 

617 and event.key() == Qt.Key.Key_Escape 

618 ): 

619 self.canvas.cancel_selection_transform() 

620 return True 

621 

622 if event.type() in ( 

623 QEvent.Type.ApplicationDeactivate, 

624 QEvent.Type.WindowDeactivate, 

625 ): 

626 self.canvas._brush_resize_anchor = None 

627 self._held_canvas_shortcut_tokens.clear() 

628 self.tool_hold.release_all() 

629 try: 

630 self.color_chart.tile_canvas.clear_hold_keys() 

631 except RuntimeError: 

632 # The chart dock can already be destroyed during app teardown. 

633 pass 

634 self._update_canvas_hold_operation() 

635 return super().eventFilter(watched, event) 

636 

637 if ( 

638 event.type() == QEvent.Type.ShortcutOverride 

639 and not getattr(event, "isAutoRepeat", lambda: False)() 

640 ): 

641 # ツールの一時切り替え:ショートカットになるキーには KeyPress が 

642 # 届かないので、押したことはここで覚える。 

643 self.tool_hold.key_pressed(self._event_key_token(event)) 

644 if event.type() in (QEvent.Type.KeyPress, QEvent.Type.KeyRelease): 

645 # ショートカット編集ダイアログ内では、入力したキーをキャンバス操作に使わない。 

646 modal = QApplication.activeModalWidget() 

647 if isinstance(modal, ShortcutDialog): 647 ↛ 648line 647 didn't jump to line 648 because the condition on line 647 was never true

648 return super().eventFilter(watched, event) 

649 if getattr(event, "isAutoRepeat", lambda: False)(): 649 ↛ 650line 649 didn't jump to line 650 because the condition on line 649 was never true

650 return super().eventFilter(watched, event) 

651 token = self._event_key_token(event) 

652 if event.type() == QEvent.Type.KeyRelease: 

653 self.tool_hold.key_released(token) 

654 self._handle_space_tap(token, event.type() == QEvent.Type.KeyPress) 

655 if event.type() == QEvent.Type.KeyPress: 

656 if token: 656 ↛ 658line 656 didn't jump to line 658 because the condition on line 656 was always true

657 self._held_canvas_shortcut_tokens.add(token) 

658 self._sync_modifier_tokens(event.modifiers()) 

659 else: 

660 if token: 660 ↛ 662line 660 didn't jump to line 662 because the condition on line 660 was always true

661 self._held_canvas_shortcut_tokens.discard(token) 

662 self._sync_modifier_tokens(event.modifiers()) 

663 # Qtの環境によっては解放イベントのmodifiersに解放前のキーが残る。 

664 if token in ("Ctrl", "Shift", "Alt", "Meta"): 

665 self._held_canvas_shortcut_tokens.discard(token) 

666 self._update_canvas_hold_operation() 

667 if token in ("Space", "Ctrl", "Shift", "Alt", "Meta"): 

668 return True 

669 return super().eventFilter(watched, event)