diff --git a/TermTk/TTkCore/TTkTerm/__init__.py b/TermTk/TTkCore/TTkTerm/__init__.py index 8c13ed97..22168d69 100644 --- a/TermTk/TTkCore/TTkTerm/__init__.py +++ b/TermTk/TTkCore/TTkTerm/__init__.py @@ -1,5 +1,5 @@ -# from .inputkey import * -# from .inputmouse import * +from .inputkey import * +from .inputmouse import * # from .colors import * from .input import * from .term import * diff --git a/TermTk/TTkGui/drag.py b/TermTk/TTkGui/drag.py index a38a2bec..ad5e6541 100644 --- a/TermTk/TTkGui/drag.py +++ b/TermTk/TTkGui/drag.py @@ -20,11 +20,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -__all__ = ['TTkDrag', 'TTkDropEvent'] +__all__ = ['TTkDrag', 'TTkDnDEvent'] from TermTk.TTkCore.helper import TTkHelper from TermTk.TTkCore.canvas import TTkCanvas from TermTk.TTkWidgets.widget import TTkWidget +from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent class _TTkDragDisplayWidget(TTkWidget): __slots__ = ('_pixmap') @@ -41,30 +42,53 @@ class _TTkDragDisplayWidget(TTkWidget): _,_,w,h = self.geometry() canvas.paintCanvas(self._pixmap, (0,0,w,h), (0,0,w,h), (0,0,w,h)) -class TTkDrag(): - __slots__ = ('_data', '_pixmap', '_showPixmap', '_hotSpot') - def __init__(self): - self._data = None +class TTkDnD(): + __slots__ = ('_data') + def __init__(self, data:object=None) -> None: + self._data = data + + def setData(self, data:object) -> None: + self._data = data + + def data(self) -> object: + return self._data + +class TTkDnDEvent(TTkDnD): + __slots__ = ('_pos', 'x', 'y') + def __init__(self, data:object=None, pos:tuple[int,int]=(0,0)) -> None: + self._pos = pos + self.x, self.y = pos + super().__init__(data) + + def setPos(self, pos:tuple[int,int]) -> None: + self._pos = pos + self.x, self.y = pos + + def pos(self) -> tuple[int,int]: + return self._pos + + def copy(self): + ret = TTkDnDEvent(self._data, self._pos) + return ret + +class TTkDrag(TTkDnD): + __slots__ = ('_pixmap', '_showPixmap', '_hotSpot') + def __init__(self, data:object=None) -> None: self._showPixmap = True self._hotSpot = (0,0) self._pixmap = _TTkDragDisplayWidget(size=(5,1)) pixmap = TTkCanvas(width=5, height=1) pixmap.drawText(pos=(0,0), text='[...]') self._pixmap.setPixmap(pixmap, self._hotSpot) + super().__init__(data) - def setData(self, data): - self._data = data - - def data(self): - return self._data - - def setHotSpot(self, x,y): + def setHotSpot(self, x,y) -> None: self._hotSpot = (x,y) - def hotSpot(self): + def hotSpot(self) -> tuple[int,int]: return self._hotSpot - def setPixmap(self, pixmap): + def setPixmap(self, pixmap:TTkWidget) -> None: if issubclass(type(pixmap),TTkWidget): canvas = pixmap.getCanvas() canvas.updateSize() @@ -74,47 +98,33 @@ class TTkDrag(): pixmap.updateSize() self._pixmap.setPixmap(pixmap, self._hotSpot) - def pixmap(self): + def pixmap(self) -> TTkWidget: return self._pixmap - def visible(self): + def visible(self) -> bool: return self._showPixmap - def showPixmap(self): + def showPixmap(self) -> None: self._showPixmap = True - def hidePixmap(self): + def hidePixmap(self) -> None: self._showPixmap = False - def exec(self): + def exec(self) -> None: TTkHelper.dndInit(self) - - @staticmethod - def copy(drag): - ret = TTkDropEvent() - ret._data = drag._data - ret._pixmap = drag._pixmap - ret._hotSpot = drag._hotSpot + + def _toDropEvent(self, pos:tuple[int,int]) -> TTkDnDEvent: + ret = TTkDnDEvent(self._data, pos) return ret - def getDragEnterEvent(self, evt): - ret = TTkDropEvent.copy(self) - ret._pos = (evt.x, evt.y) - ret.x = evt.x - ret.y = evt.y - return ret + def getDragEnterEvent(self, evt:TTkMouseEvent) -> TTkDnDEvent: + return self._toDropEvent((evt.x, evt.y)) - def getDragLeaveEvent(self, evt): - return self.getDragEnterEvent(evt) + def getDragLeaveEvent(self, evt:TTkMouseEvent) -> TTkDnDEvent: + return self._toDropEvent((evt.x, evt.y)) - def getDragMoveEvent(self, evt): - return self.getDragEnterEvent(evt) - - def getDropEvent(self, evt): - return self.getDragEnterEvent(evt) - - -class TTkDropEvent(TTkDrag): - __slots__ = ('_pos', 'x', 'y') + def getDragMoveEvent(self, evt:TTkMouseEvent) -> TTkDnDEvent: + return self._toDropEvent((evt.x, evt.y)) - def pos(self): return self._pos + def getDropEvent(self, evt:TTkMouseEvent) -> TTkDnDEvent: + return self._toDropEvent((evt.x, evt.y)) diff --git a/TermTk/TTkTemplates/dragevents.py b/TermTk/TTkTemplates/dragevents.py index aa07f212..7b1a848b 100644 --- a/TermTk/TTkTemplates/dragevents.py +++ b/TermTk/TTkTemplates/dragevents.py @@ -23,58 +23,56 @@ __all__ = ['TDragEvents'] class TDragEvents(): - def __init__(self) -> None: pass - - def dragEnterEvent(self, evt) -> bool: + def dragEnterEvent(self, evt:"TTkDnDEvent") -> bool: ''' This event handler, can be reimplemented in a subclass to receive drag events for the widget. .. note:: Reimplement this function to handle this event :param evt: The drop event - :type evt: :py:class:`TTkDropEvent` + :type evt: :py:class:`TTkDnDEvent` :return: **True** if the event has been handled :rtype: bool ''' return False - def dragLeaveEvent(self, evt) -> bool: + def dragLeaveEvent(self, evt:"TTkDnDEvent") -> bool: ''' This event handler, can be reimplemented in a subclass to receive drag events for the widget. .. note:: Reimplement this function to handle this event :param evt: The drop event - :type evt: :py:class:`TTkDropEvent` + :type evt: :py:class:`TTkDnDEvent` :return: **True** if the event has been handled :rtype: bool ''' return False - def dragMoveEvent(self, evt) -> bool: + def dragMoveEvent(self, evt:"TTkDnDEvent") -> bool: ''' This event handler, can be reimplemented in a subclass to receive drag events for the widget. .. note:: Reimplement this function to handle this event :param evt: The drop event - :type evt: :py:class:`TTkDropEvent` + :type evt: :py:class:`TTkDnDEvent` :return: **True** if the event has been handled :rtype: bool ''' return False - def dropEvent(self, evt) -> bool: + def dropEvent(self, evt:"TTkDnDEvent") -> bool: ''' This event handler, can be reimplemented in a subclass to receive drag events for the widget. .. note:: Reimplement this function to handle this event :param evt: The drop event - :type evt: :py:class:`TTkDropEvent` + :type evt: :py:class:`TTkDnDEvent` :return: **True** if the event has been handled :rtype: bool diff --git a/TermTk/TTkWidgets/listwidget.py b/TermTk/TTkWidgets/listwidget.py index 8980c2c0..4bac2315 100644 --- a/TermTk/TTkWidgets/listwidget.py +++ b/TermTk/TTkWidgets/listwidget.py @@ -31,7 +31,10 @@ from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal from TermTk.TTkCore.color import TTkColor from TermTk.TTkCore.canvas import TTkCanvas from TermTk.TTkCore.string import TTkString -from TermTk.TTkGui.drag import TTkDrag +from TermTk.TTkCore.TTkTerm.inputkey import TTkKeyEvent +from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent +from TermTk.TTkGui.drag import TTkDrag, TTkDnDEvent + from TermTk.TTkWidgets.widget import TTkWidget from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView @@ -79,7 +82,7 @@ class TTkAbstractListItem(TTkWidget): self._data = data self.update() - def mousePressEvent(self, evt) -> bool: + def mousePressEvent(self, evt:TTkMouseEvent) -> bool: self.listItemClicked.emit(self) return True @@ -363,7 +366,7 @@ class TTkListWidget(TTkAbstractScrollView): elif index <= offy: self.viewMoveTo(offx, index) - def mouseDragEvent(self, evt) -> bool: + def mouseDragEvent(self, evt:TTkMouseEvent) -> bool: if not(self._dndMode & TTkK.DragDropMode.AllowDrag): return False if not (items:=self._selectedItems.copy()): @@ -388,26 +391,26 @@ class TTkListWidget(TTkAbstractScrollView): drag.exec() return True - def dragEnterEvent(self, evt): + def dragEnterEvent(self, evt:TTkDnDEvent) -> bool: if not(self._dndMode & TTkK.DragDropMode.AllowDrop): return False if issubclass(type(evt.data()),TTkListWidget._DropListData): return self.dragMoveEvent(evt) return False - def dragMoveEvent(self, evt): + def dragMoveEvent(self, evt:TTkDnDEvent) -> bool: offx,offy = self.getViewOffsets() y=min(evt.y+offy,len(self._items)) self._dragPos = (offx+evt.x, y) self.update() return True - def dragLeaveEvent(self, evt): + def dragLeaveEvent(self, evt:TTkDnDEvent) -> bool: self._dragPos = None self.update() return True - def dropEvent(self, evt) -> bool: + def dropEvent(self, evt:TTkDnDEvent) -> bool: if not(self._dndMode & TTkK.DragDropMode.AllowDrop): return False self._dragPos = None @@ -424,7 +427,7 @@ class TTkListWidget(TTkAbstractScrollView): return True return False - def keyEvent(self, evt): + def keyEvent(self, evt:TTkKeyEvent) -> bool: if not self._highlighted: return False if ( evt.type == TTkK.Character and evt.key==" " ) or \ ( evt.type == TTkK.SpecialKey and evt.key == TTkK.Key_Enter ): diff --git a/TermTk/TTkWidgets/tabwidget.py b/TermTk/TTkWidgets/tabwidget.py index ce958ab2..912e1ca9 100644 --- a/TermTk/TTkWidgets/tabwidget.py +++ b/TermTk/TTkWidgets/tabwidget.py @@ -29,14 +29,18 @@ from TermTk.TTkCore.cfg import TTkCfg from TermTk.TTkCore.color import TTkColor from TermTk.TTkCore.canvas import TTkCanvas from TermTk.TTkCore.string import TTkString -from TermTk.TTkGui.drag import TTkDrag from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal +from TermTk.TTkCore.TTkTerm.inputkey import TTkKeyEvent +from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent + +from TermTk.TTkGui.drag import TTkDrag, TTkDnDEvent + from TermTk.TTkWidgets.widget import TTkWidget from TermTk.TTkWidgets.container import TTkContainer from TermTk.TTkWidgets.spacer import TTkSpacer from TermTk.TTkWidgets.frame import TTkFrame -from TermTk.TTkWidgets.button import TTkButton from TermTk.TTkWidgets.menubar import TTkMenuBarButton + from TermTk.TTkLayouts.boxlayout import TTkHBoxLayout from TermTk.TTkLayouts.gridlayout import TTkGridLayout @@ -102,11 +106,11 @@ class _TTkTabColorButton(TTkContainer): def text(self) -> TTkString: return self._text - def mouseReleaseEvent(self, evt): + def mouseReleaseEvent(self, evt:TTkMouseEvent) -> bool: self.clicked.emit() return True - def keyEvent(self, evt): + def keyEvent(self, evt:TTkKeyEvent) -> bool: if ( evt.type == TTkK.Character and evt.key==" " ) or \ ( evt.type == TTkK.SpecialKey and evt.key == TTkK.Key_Enter ): self._keyPressed = True @@ -165,7 +169,7 @@ class TTkTabButton(_TTkTabColorButton): # This is a hack to force the action aftet the keypress # And not key release as normally happen to the button - def mousePressEvent(self, evt): + def mousePressEvent(self, evt:TTkMouseEvent) -> bool: x,y = evt.x,evt.y w,h = self.size() self._closeButtonPressed = False @@ -176,7 +180,7 @@ class TTkTabButton(_TTkTabColorButton): self._closeButtonPressed = True return True return super().mouseReleaseEvent(evt) - def mouseReleaseEvent(self, evt): + def mouseReleaseEvent(self, evt:TTkMouseEvent) -> bool: x,y = evt.x,evt.y w,h = self.size() if self._closable and y == (1 if self._border else 0) and w-4<=x bool: + def mouseDragEvent(self, evt:TTkMouseEvent) -> bool: drag = TTkDrag() self._closeButtonPressed = False if tb := self.parentWidget(): @@ -266,11 +270,11 @@ class _TTkTabScrollerButton(_TTkTabColorButton): # This is a hack to force the action aftet the keypress # And not key release as normally happen to the button - def mousePressEvent(self, evt): + def mousePressEvent(self, evt:TTkMouseEvent) -> bool: return super().mouseReleaseEvent(evt) - def mouseReleaseEvent(self, evt): + def mouseReleaseEvent(self, evt:TTkMouseEvent) -> bool: return False - def mouseTapEvent(self, evt) -> bool: + def mouseTapEvent(self, evt:TTkMouseEvent) -> bool: self.clicked.emit() return True @@ -504,14 +508,14 @@ class TTkTabBar(TTkContainer): self._highlighted = self._currentIndex self._updateTabs() - def wheelEvent(self, evt): + def wheelEvent(self, evt:TTkMouseEvent) -> bool: if evt.evt == TTkK.WHEEL_Up: self._moveToTheLeft() else: self._andMoveToTheRight() return True - def keyEvent(self, evt): + def keyEvent(self, evt:TTkKeyEvent) -> bool: if evt.type == TTkK.SpecialKey: if evt.key == TTkK.Key_Right: self._highlighted = min(self._highlighted+1,len(self._tabButtons)-1) @@ -656,10 +660,10 @@ class TTkTabWidget(TTkFrame): else: widget.hide() - def keyEvent(self, evt) -> bool: + def keyEvent(self, evt:TTkKeyEvent) -> bool: return self._tabBar.keyEvent(evt) - def dropEvent(self, evt) -> bool: + def dropEvent(self, evt:TTkDnDEvent) -> bool: data = evt.data() x, y = evt.x, evt.y if not issubclass(type (data),_TTkTabWidgetDragData): diff --git a/demo/showcase/dragndrop.py b/demo/showcase/dragndrop.py index a7b6602a..6ab79d68 100755 --- a/demo/showcase/dragndrop.py +++ b/demo/showcase/dragndrop.py @@ -31,8 +31,8 @@ sys.path.append(os.path.join(sys.path[0],'../..')) import TermTk as ttk class DragThing(ttk.TTkFrame): - def __init__(self, *args, **kwargs): - ttk.TTkFrame.__init__(self, *args, **kwargs) + def __init__(self, **kwargs): + ttk.TTkFrame.__init__(self, **kwargs) # Define and place 4 images with different Hue Color rotation ttk.TTkImage(parent=self, pos=( 0, 0), data=ttk.TTkAbout.peppered, rasteriser=ttk.TTkImage.QUADBLOCK) ttk.TTkImage(parent=self, pos=( 0,10), data=ttk.TTkAbout.peppered, rasteriser=ttk.TTkImage.QUADBLOCK).rotHue(60) @@ -41,22 +41,23 @@ class DragThing(ttk.TTkFrame): self.setMaximumWidth(30) self.setMinimumWidth(30) - def mouseDragEvent(self, evt) -> bool: - ttk.TTkLog.debug("Start DnD") - drag = ttk.TTkDrag() - data = ttk.TTkImage(data=ttk.TTkAbout.peppered, rasteriser=ttk.TTkImage.QUADBLOCK) - # Change color if the drag start over the side images, - # based on the same Hue rotation defined in the init - if evt.x <= 15 and evt.y > 10: data.rotHue(60) - elif evt.x > 15 and evt.y <= 10: data.rotHue(90) - elif evt.x > 15 and evt.y > 10: data.rotHue(200) - drag.setPixmap(data) - drag.setData(data) - drag.exec() + def mouseDragEvent(self, evt:ttk.TTkMouseEvent) -> bool: + if evt.key == ttk. TTkMouseEvent.LeftButton: + ttk.TTkLog.debug("Start DnD") + drag = ttk.TTkDrag() + data = ttk.TTkImage(data=ttk.TTkAbout.peppered, rasteriser=ttk.TTkImage.QUADBLOCK) + # Change color if the drag start over the side images, + # based on the same Hue rotation defined in the init + if evt.x <= 15 and evt.y > 10: data.rotHue(60) + elif evt.x > 15 and evt.y <= 10: data.rotHue(90) + elif evt.x > 15 and evt.y > 10: data.rotHue(200) + drag.setPixmap(data) + drag.setData(data) + drag.exec() return True class DropThings(ttk.TTkFrame): - def dropEvent(self, evt) -> bool: + def dropEvent(self, evt:ttk.TTkDnDEvent) -> bool: ttk.TTkLog.debug(f"Drop ({self.title()}) -> pos={evt.pos()}") data = evt.data() if issubclass(type(data),ttk.TTkWidget): diff --git a/tests/t.ui/test.ui.017.Drag.Drop.001.py b/tests/t.ui/test.ui.017.Drag.Drop.001.py index a2472a0f..86f4f32b 100755 --- a/tests/t.ui/test.ui.017.Drag.Drop.001.py +++ b/tests/t.ui/test.ui.017.Drag.Drop.001.py @@ -28,26 +28,37 @@ sys.path.append(os.path.join(sys.path[0],'../..')) import TermTk as ttk class DragDrop(ttk.TTkFrame): - def mouseDragEvent(self, evt) -> bool: - ttk.TTkLog.debug("Start DnD") - drag = ttk.TTkDrag() - drag.setData(f"Test Drag ({self.title()})") - drag.exec() + def mouseDragEvent(self, evt:ttk. TTkMouseEvent) -> bool: + if evt.key == ttk. TTkMouseEvent.LeftButton: + ttk.TTkLog.debug("Start Left DnD") + drag = ttk.TTkDrag() + drag.setData(f"Test Drag ({self.title()})") + drag.exec() + elif evt.key == ttk. TTkMouseEvent.RightButton: + ttk.TTkLog.debug("Start Right DnD") + drag = ttk.TTkDrag() + drag.setData(f"Test Drag ({self.title()})") + drag.exec() + elif evt.key == ttk. TTkMouseEvent.MidButton: + ttk.TTkLog.debug("Start Middle DnD") + drag = ttk.TTkDrag() + drag.setData(f"Test Drag ({self.title()})") + drag.exec() return True - def dragEnterEvent(self, evt) -> bool: + def dragEnterEvent(self, evt:ttk.TTkDnDEvent) -> bool: ttk.TTkLog.debug(f"Drag Enter ({self.title()}) -> {evt.data()}, pos={evt.pos()}") return True - def dragLeaveEvent(self, evt) -> bool: + def dragLeaveEvent(self, evt:ttk.TTkDnDEvent) -> bool: ttk.TTkLog.debug(f"Drag Leave ({self.title()}) -> {evt.data()}, pos={evt.pos()}") return True - def dragMoveEvent(self, evt) -> bool: + def dragMoveEvent(self, evt:ttk.TTkDnDEvent) -> bool: ttk.TTkLog.debug(f"Drag Move ({self.title()}) -> {evt.data()}, pos={evt.pos()}") return True - def dropEvent(self, evt) -> bool: + def dropEvent(self, evt:ttk.TTkDnDEvent) -> bool: ttk.TTkLog.debug(f"Drop ({self.title()}) -> {evt.data()}, pos={evt.pos()}") return True