From 1014e61fbde7b048f6b8933c628986b42f2dc0b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Tue, 7 Dec 2021 15:18:10 +0100 Subject: [PATCH 1/4] :tada: Add selection on arrow navigation --- opencodeblocks/graphics/view.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/opencodeblocks/graphics/view.py b/opencodeblocks/graphics/view.py index ea8801f9..6d42304e 100644 --- a/opencodeblocks/graphics/view.py +++ b/opencodeblocks/graphics/view.py @@ -203,10 +203,11 @@ def moveViewOnArrow(self, event: QKeyEvent) -> bool: Returns True if the event was handled. """ # The focusItem has priority for this event - if self.scene().focusItem() is not None: - return False - if len(self.scene().selectedItems()) > 0: + n_selected_items = len(self.scene().selectedItems()) + if n_selected_items > 1: return False + if n_selected_items == 1: + self.scene().clearSelection() key_id = event.key() items = self.scene().items() @@ -248,6 +249,12 @@ def moveViewOnArrow(self, event: QKeyEvent) -> bool: block_center_x, block_center_y, _, _, _ = dist_array[0] + item_to_navigate = self.scene().itemAt( + block_center_x, block_center_y, self.transform() + ) + if isinstance(item_to_navigate.parentItem(), OCBBlock): + item_to_navigate.parentItem().setSelected(True) + self.centerView(block_center_x, block_center_y) return True From c80130487e41a2ed844e9e774e21708b88e749c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Tue, 7 Dec 2021 20:27:44 +0100 Subject: [PATCH 2/4] :tada: Improve navigation --- opencodeblocks/graphics/view.py | 79 ++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/opencodeblocks/graphics/view.py b/opencodeblocks/graphics/view.py index 6d42304e..61521ddc 100644 --- a/opencodeblocks/graphics/view.py +++ b/opencodeblocks/graphics/view.py @@ -7,7 +7,7 @@ import os from typing import List, Tuple -from PyQt5.QtCore import QEvent, QPointF, Qt +from PyQt5.QtCore import QEvent, QPoint, QPointF, Qt from PyQt5.QtGui import QKeyEvent, QMouseEvent, QPainter, QWheelEvent, QContextMenuEvent from PyQt5.QtWidgets import QGraphicsView, QMenu from PyQt5.sip import isdeleted @@ -206,12 +206,24 @@ def moveViewOnArrow(self, event: QKeyEvent) -> bool: n_selected_items = len(self.scene().selectedItems()) if n_selected_items > 1: return False - if n_selected_items == 1: - self.scene().clearSelection() - key_id = event.key() - items = self.scene().items() - code_blocks = [i for i in items if isinstance(i, OCBBlock)] + code_blocks = [ + i + for i in self.scene().items() + if isinstance(i, OCBBlock) and not i.isSelected() + ] + + if n_selected_items == 1 and isinstance( + self.scene().selectedItems()[0], OCBBlock + ): + selected_item = self.scene().selectedItems()[0] + reference = QPoint( + selected_item.x() + selected_item.width / 2, + selected_item.y() + selected_item.height / 2, + ) + self.scene().clearSelection() + else: + reference = None # Pick the block with the center distance (x,y) such that: # ||(x,y)|| is minimal but not too close to 0, where ||.|| is the infinity norm @@ -222,32 +234,45 @@ def moveViewOnArrow(self, event: QKeyEvent) -> bool: for block in code_blocks: block_center_x = block.x() + block.width / 2 block_center_y = block.y() + block.height / 2 - xdist, ydist = self.getDistanceToCenter(block_center_x, block_center_y) - dist_array.append( - ( - block_center_x, - block_center_y, - xdist, - ydist, - max(abs(xdist), abs(ydist)), + if reference is None: + xdist, ydist = self.getDistanceToCenter(block_center_x, block_center_y) + else: + xdist, ydist = ( + reference.x() - block_center_x, + reference.y() - block_center_y, ) - ) + dist_array.append((block_center_x, block_center_y, -xdist, -ydist)) + + def in_region(x, y, key): + up_right = x / self.width() - y / self.height() >= 0 + down_right = x / self.width() + y / self.height() >= 0 + if key == Qt.Key.Key_Up: + return up_right and not down_right + if key == Qt.Key.Key_Down: + return not up_right and down_right + if key == Qt.Key.Key_Left: + return not up_right and not down_right + if key == Qt.Key.Key_Right: + return up_right and down_right - if key_id == Qt.Key.Key_Up: - dist_array = filter(lambda pos: pos[3] > 1, dist_array) - if key_id == Qt.Key.Key_Down: - dist_array = filter(lambda pos: pos[3] < -1, dist_array) - if key_id == Qt.Key.Key_Right: - dist_array = filter(lambda pos: pos[2] < -1, dist_array) - if key_id == Qt.Key.Key_Left: - dist_array = filter(lambda pos: pos[2] > 1, dist_array) + key_id = event.key() + dist_array = filter(lambda pos: in_region(pos[2], pos[3], key_id), dist_array) dist_array = list(dist_array) - - if len(dist_array) <= 0: + if len(dist_array) == 0: return False - dist_array.sort(key=lambda d: d[4]) - block_center_x, block_center_y, _, _, _ = dist_array[0] + def oriented_distance(x, y, key): + if key == Qt.Key.Key_Up: + return -y / self.height() + (x / self.width()) ** 2 + if key == Qt.Key.Key_Down: + return y / self.height() + (x / self.width()) ** 2 + if key == Qt.Key.Key_Left: + return -x / self.width() + (y / self.height()) ** 2 + if key == Qt.Key.Key_Right: + return x / self.width() + (y / self.height()) ** 2 + + dist_array.sort(key=lambda pos: oriented_distance(pos[2], pos[3], key_id)) + block_center_x, block_center_y, _, _ = dist_array[0] item_to_navigate = self.scene().itemAt( block_center_x, block_center_y, self.transform() From 5ad56c404ea0317a13553a9cc96f0625839d988f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Tue, 7 Dec 2021 21:32:24 +0100 Subject: [PATCH 3/4] :beetle: Fix arrow trigger while source_editing --- opencodeblocks/graphics/view.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/opencodeblocks/graphics/view.py b/opencodeblocks/graphics/view.py index 61521ddc..26e9901c 100644 --- a/opencodeblocks/graphics/view.py +++ b/opencodeblocks/graphics/view.py @@ -16,7 +16,7 @@ from opencodeblocks.scene import OCBScene from opencodeblocks.graphics.socket import OCBSocket from opencodeblocks.graphics.edge import OCBEdge -from opencodeblocks.blocks import OCBBlock +from opencodeblocks.blocks import OCBBlock, OCBCodeBlock EPS: float = 1e-10 # To check if blocks are of size 0 @@ -202,7 +202,12 @@ def moveViewOnArrow(self, event: QKeyEvent) -> bool: OCBView reaction to an arrow key being pressed. Returns True if the event was handled. """ - # The focusItem has priority for this event + # The focusItem has priority for this event if it is a source editor + if self.scene().focusItem() is not None: + parent = self.scene().focusItem().parentItem() + if isinstance(parent, OCBCodeBlock) and parent.source_editor.hasFocus(): + return False + n_selected_items = len(self.scene().selectedItems()) if n_selected_items > 1: return False From b1976359a1cf96df5546e955a5b4bc474f247933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Tue, 7 Dec 2021 21:41:28 +0100 Subject: [PATCH 4/4] :wrench: Refactor comments and oriented_distance --- opencodeblocks/graphics/view.py | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/opencodeblocks/graphics/view.py b/opencodeblocks/graphics/view.py index 26e9901c..de83bdeb 100644 --- a/opencodeblocks/graphics/view.py +++ b/opencodeblocks/graphics/view.py @@ -218,6 +218,7 @@ def moveViewOnArrow(self, event: QKeyEvent) -> bool: if isinstance(i, OCBBlock) and not i.isSelected() ] + reference = None if n_selected_items == 1 and isinstance( self.scene().selectedItems()[0], OCBBlock ): @@ -227,13 +228,6 @@ def moveViewOnArrow(self, event: QKeyEvent) -> bool: selected_item.y() + selected_item.height / 2, ) self.scene().clearSelection() - else: - reference = None - - # Pick the block with the center distance (x,y) such that: - # ||(x,y)|| is minimal but not too close to 0, where ||.|| is the infinity norm - # This norm was choosen because the movements it generates feel natural. - # x or y has the correct sign (depends on the key pressed) dist_array = [] for block in code_blocks: @@ -242,10 +236,8 @@ def moveViewOnArrow(self, event: QKeyEvent) -> bool: if reference is None: xdist, ydist = self.getDistanceToCenter(block_center_x, block_center_y) else: - xdist, ydist = ( - reference.x() - block_center_x, - reference.y() - block_center_y, - ) + xdist = reference.x() - block_center_x + ydist = reference.y() - block_center_y dist_array.append((block_center_x, block_center_y, -xdist, -ydist)) def in_region(x, y, key): @@ -267,14 +259,10 @@ def in_region(x, y, key): return False def oriented_distance(x, y, key): - if key == Qt.Key.Key_Up: - return -y / self.height() + (x / self.width()) ** 2 - if key == Qt.Key.Key_Down: - return y / self.height() + (x / self.width()) ** 2 - if key == Qt.Key.Key_Left: - return -x / self.width() + (y / self.height()) ** 2 - if key == Qt.Key.Key_Right: - return x / self.width() + (y / self.height()) ** 2 + if key in (Qt.Key.Key_Down, Qt.Key.Key_Up): + return abs(y) / self.height() + (x / self.width()) ** 2 + if key in (Qt.Key.Key_Left, Qt.Key.Key_Right): + return abs(x) / self.width() + (y / self.height()) ** 2 dist_array.sort(key=lambda pos: oriented_distance(pos[2], pos[3], key_id)) block_center_x, block_center_y, _, _ = dist_array[0]