From 34e9a39dee0cc31f10bd591f459c9b194f6f8b2c Mon Sep 17 00:00:00 2001 From: Pier CeccoPierangioliEugenio Date: Sat, 9 Aug 2025 09:18:59 +0100 Subject: [PATCH] feat(Tab): support for a single drop of multiple new tabs (#437) --- libs/pyTermTk/TermTk/TTkCore/constant.py | 2 +- libs/pyTermTk/TermTk/TTkWidgets/kodetab.py | 48 ++++++++++--------- libs/pyTermTk/TermTk/TTkWidgets/list_.py | 6 +-- libs/pyTermTk/TermTk/TTkWidgets/listwidget.py | 6 +-- libs/pyTermTk/TermTk/TTkWidgets/tabwidget.py | 45 ++++++++++------- 5 files changed, 61 insertions(+), 46 deletions(-) diff --git a/libs/pyTermTk/TermTk/TTkCore/constant.py b/libs/pyTermTk/TermTk/TTkCore/constant.py index 29164af4..e3313fb1 100644 --- a/libs/pyTermTk/TermTk/TTkCore/constant.py +++ b/libs/pyTermTk/TermTk/TTkCore/constant.py @@ -100,7 +100,7 @@ class TTkConstant: HEADER = 0x0020 FOOTER = 0x0040 - class SelectionMode(int): + class SelectionMode(IntEnum): ''' This class type indicates how the view responds to user selections. diff --git a/libs/pyTermTk/TermTk/TTkWidgets/kodetab.py b/libs/pyTermTk/TermTk/TTkWidgets/kodetab.py index 5f315ff6..4637384a 100644 --- a/libs/pyTermTk/TermTk/TTkWidgets/kodetab.py +++ b/libs/pyTermTk/TermTk/TTkWidgets/kodetab.py @@ -22,7 +22,7 @@ __all__ = ['TTkKodeTab'] -from typing import Callable, Iterator, Tuple +from typing import Callable, Iterator, Tuple, List from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.helper import TTkHelper @@ -121,18 +121,24 @@ class _TTkKodeTab(TTkTabWidget): self.update() return True + def _dropNewKodeTab(self, y:int, data:_TTkNewTabWidgetDragData) -> bool: + pass + def dropEvent(self, evt:TTkDnDEvent) -> bool: self._frameOverlay = None x,y = evt.x, evt.y data = evt.data() - if issubclass(type(data),_TTkNewTabWidgetDragData): - tw = None - elif issubclass(type(data),_TTkTabWidgetDragData): + if isinstance(data, _TTkTabWidgetDragData): tw = data.tabWidget() + elif isinstance(data, _TTkNewTabWidgetDragData): + tw = None + data = [data] + elif isinstance(data,list) and all(isinstance(_d,_TTkNewTabWidgetDragData) for _d in data): + tw = None else: return False - def _processDrop(widget, label, orientation, offset): + def _processDrop(data:List[Tuple[TTkWidget,TTkString]], orientation, offset): fwold = self._baseWidget._getFirstWidget() splitter = self.parentWidget() index = splitter.indexOf(self) @@ -144,34 +150,31 @@ class _TTkKodeTab(TTkTabWidget): splitter.insertWidget(index+offset, kt:=_TTkKodeTab(baseWidget=self._baseWidget, border=self.border(), barType=self._barType, closable=self.tabsClosable())) kt._dropEventProxy = self._dropEventProxy kt.kodeTabCloseRequested.connect(self._baseWidget._handleKodeTabCloseRequested) - ret = kt.addTab(widget,label) - self._baseWidget.tabAdded.emit(kt, ret) + for (_w,_l) in data: + _ret = kt.addTab(_w,_l) + self._baseWidget.tabAdded.emit(kt, _ret) if fwold!=(fwnew := self._baseWidget._getFirstWidget()) and fwold._hasMenu(): fwnew._importMenu(fwold) ret = True if y<3: ret = super().dropEvent(evt) - elif issubclass(type(data),_TTkNewTabWidgetDragData): - tw = None - widget = data.widget() - tabData = data.data() - label = data.label() - closable = data.closable() + elif isinstance(data,list) and all(isinstance(_d,_TTkNewTabWidgetDragData) for _d in data): w,h = self.size() h-=3 y-=3 + dropData = [(_d.widget(),_d.label()) for _d in data] if xw*3//4: - _processDrop(widget, label, TTkK.HORIZONTAL, 1) + _processDrop(dropData, TTkK.HORIZONTAL, 1) elif yh*3//4: - _processDrop(widget, label, TTkK.VERTICAL, 1) + _processDrop(dropData, TTkK.VERTICAL, 1) else: ret = super().dropEvent(evt) - elif issubclass(type(data),_TTkTabWidgetDragData): + elif isinstance(data,_TTkTabWidgetDragData): tb = data.tabButton() tw = data.tabWidget() w,h = self.size() @@ -180,19 +183,20 @@ class _TTkKodeTab(TTkTabWidget): index = tw._tabBar._tabButtons.index(tb) widget = tw._tabWidgets[index] label = tb.text() + dropData = [(widget,label)] if xw*3//4: tw.removeTab(index) - _processDrop(widget, label, TTkK.HORIZONTAL, 1) + _processDrop(dropData, TTkK.HORIZONTAL, 1) elif yh*3//4: tw.removeTab(index) - _processDrop(widget, label, TTkK.VERTICAL, 1) + _processDrop(dropData, TTkK.VERTICAL, 1) else: ret = super().dropEvent(evt) diff --git a/libs/pyTermTk/TermTk/TTkWidgets/list_.py b/libs/pyTermTk/TermTk/TTkWidgets/list_.py index a824587c..67338ccb 100644 --- a/libs/pyTermTk/TermTk/TTkWidgets/list_.py +++ b/libs/pyTermTk/TermTk/TTkWidgets/list_.py @@ -54,7 +54,7 @@ class TTkList(TTkAbstractScrollArea): def __init__(self, *, listWidget:TTkListWidget=None, - selectionMode:int=TTkK.SingleSelection, + selectionMode:TTkK.SelectionMode=TTkK.SingleSelection, dragDropMode:TTkK.DragDropMode=TTkK.DragDropMode.NoDragDrop, showSearch:bool=True, **kwargs) -> None: @@ -195,14 +195,14 @@ class TTkList(TTkAbstractScrollArea): removeItems ''' return self._listView.removeItems(items=items) - def selectionMode(self): + def selectionMode(self) -> TTkK.SelectionMode: ''' .. seealso:: this method is forwarded to :py:meth:`TTkListWidget.selectionMode` selectionMode ''' return self._listView.selectionMode() - def setSelectionMode(self, mode): + def setSelectionMode(self, mode:TTkK.SelectionMode) -> None: ''' .. seealso:: this method is forwarded to :py:meth:`TTkListWidget.setSelectionMode` diff --git a/libs/pyTermTk/TermTk/TTkWidgets/listwidget.py b/libs/pyTermTk/TermTk/TTkWidgets/listwidget.py index 4ffd341d..34f1baaf 100644 --- a/libs/pyTermTk/TermTk/TTkWidgets/listwidget.py +++ b/libs/pyTermTk/TermTk/TTkWidgets/listwidget.py @@ -201,7 +201,7 @@ class TTkListWidget(TTkAbstractScrollView): '_itemClicked', '_textClicked', '_searchModified') def __init__(self, *, items:list[str]=[], - selectionMode:int=TTkK.SelectionMode.SingleSelection, + selectionMode:TTkK.SelectionMode=TTkK.SelectionMode.SingleSelection, dragDropMode:TTkK.DragDropMode=TTkK.DragDropMode.NoDragDrop, showSearch:bool=True, **kwargs) -> None: @@ -309,11 +309,11 @@ class TTkListWidget(TTkAbstractScrollView): '''setDragDropMode''' self._dndMode = dndMode - def selectionMode(self): + def selectionMode(self) -> TTkK.SelectionMode: '''selectionMode''' return self._selectionMode - def setSelectionMode(self, mode): + def setSelectionMode(self, mode:TTkK.SelectionMode) -> None: '''setSelectionMode''' self._selectionMode = mode diff --git a/libs/pyTermTk/TermTk/TTkWidgets/tabwidget.py b/libs/pyTermTk/TermTk/TTkWidgets/tabwidget.py index c33f6d71..bbc1d5b0 100644 --- a/libs/pyTermTk/TermTk/TTkWidgets/tabwidget.py +++ b/libs/pyTermTk/TermTk/TTkWidgets/tabwidget.py @@ -23,6 +23,7 @@ __all__ = ['TTkTabButton', 'TTkTabBar', 'TTkTabWidget', 'TTkBarType'] from enum import Enum +from typing import List, Tuple from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.helper import TTkHelper @@ -814,10 +815,29 @@ class TTkTabWidget(TTkFrame): def keyEvent(self, evt:TTkKeyEvent) -> bool: return self._tabBar.keyEvent(evt) + def _dropNewTab(self, x:int, y:int, data:_TTkNewTabWidgetDragData) -> None: + w = data.widget() + d = data.data() + l = data.label() + c = data.closable() + if y < 3: + tbx = self._tabBar.x() + newIndex = 0 + for b in self._tabBar._tabButtons: + if tbx+b.x()+b.width()/2 < x: + newIndex += 1 + self.insertTab(newIndex, w, l, d, c) + self.setCurrentIndex(newIndex) + else: + self.addTab(w, l, d, c) + self.setCurrentIndex(len(self._tabBar._tabButtons)-1) + def dropEvent(self, evt:TTkDnDEvent) -> bool: data = evt.data() x, y = evt.x, evt.y - if issubclass(type(data),_TTkTabWidgetDragData): + if not data: + return False + elif isinstance(data,_TTkTabWidgetDragData): tb = data.tabButton() tw = data.tabWidget() index = tw._tabBar._tabButtons.index(tb) @@ -846,22 +866,13 @@ class TTkTabWidget(TTkFrame): self._tabChanged(newIndex) TTkLog.debug(f"Drop -> pos={evt.pos()}") return True - elif issubclass(type(data),_TTkNewTabWidgetDragData): - w = data.widget() - d = data.data() - l = data.label() - c = data.closable() - if y < 3: - tbx = self._tabBar.x() - newIndex = 0 - for b in self._tabBar._tabButtons: - if tbx+b.x()+b.width()/2 < x: - newIndex += 1 - self.insertTab(newIndex, w, l, d, c) - self.setCurrentIndex(newIndex) - else: - self.addTab(w, l, d, c) - self.setCurrentIndex(len(self._tabBar._tabButtons)-1) + elif isinstance(data,_TTkNewTabWidgetDragData): + self._dropNewTab(x,y,data) + TTkLog.debug(f"Drop -> pos={evt.pos()}") + return True + elif isinstance(data,list) and all(isinstance(_d,_TTkNewTabWidgetDragData) for _d in data): + for _d in data: + self._dropNewTab(x,y,_d) TTkLog.debug(f"Drop -> pos={evt.pos()}") return True return False