Browse Source

Finalised the shortcut feature, adapted the menubar to use it

Dispose_Widget
Eugenio Parodi 2 years ago
parent
commit
b2ebdae6e0
  1. 1
      TermTk/TTkCore/__init__.py
  2. 24
      TermTk/TTkCore/helper.py
  3. 14
      TermTk/TTkCore/shortcut.py
  4. 17
      TermTk/TTkCore/string.py
  5. 21
      TermTk/TTkWidgets/menu.py
  6. 15
      TermTk/TTkWidgets/menubar.py
  7. 101
      tests/t.ui/test.ui.031.shortcut.02.menu.py

1
TermTk/TTkCore/__init__.py

@ -8,6 +8,7 @@ from .propertyanimation import *
from .ttk import *
from .canvas import *
from .color import *
from .shortcut import *
from .string import *
from .timer import *
from .filebuffer import *

24
TermTk/TTkCore/helper.py

@ -53,26 +53,6 @@ class TTkHelper:
widget.move(x,y)
_overlay = []
class _Shortcut():
__slots__ = ('_letter','_widget')
def __init__(self, letter, widget):
self._letter = letter.lower()
self._widget = widget
_shortcut = []
@staticmethod
def addShortcut(widget, letter):
TTkHelper._shortcut.append(TTkHelper._Shortcut(letter, widget))
@staticmethod
def execShortcut(letter, widget=None):
if not isinstance(letter, str): return
for sc in TTkHelper._shortcut:
if sc._letter == letter.lower() and sc._widget.isVisibleAndParent():
if not widget or TTkHelper.isParent(widget, sc._widget):
sc._widget.shortcutEvent()
return
@staticmethod
def updateAll():
if TTkHelper._rootWidget:
@ -292,8 +272,8 @@ class TTkHelper:
# Build a list of buffers to be repainted
updateWidgetsBk = TTkHelper._updateWidget.copy()
updateBuffers = TTkHelper._updateBuffer.copy()
TTkHelper._updateWidget = set()
TTkHelper._updateBuffer = set()
TTkHelper._updateWidget.clear()
TTkHelper._updateBuffer.clear()
updateWidgets = set()
# TTkLog.debug(f"{len(TTkHelper._updateBuffer)} {len(TTkHelper._updateWidget)}")

14
TermTk/TTkCore/shortcut.py

@ -22,6 +22,7 @@
__all__ = ['TTkShortcut']
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.helper import TTkHelper
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
@ -41,6 +42,12 @@ class TTkKeySequence():
key &= ~(TTkK.CTRL|TTkK.ALT|TTkK.SHIFT|TTkK.META)
t = TTkK.SpecialKey if mod else TTkK.Character
self._key = TTkKeyEvent(type=t, key=key, mod=mod, code="")
if mod:
self._key = TTkKeyEvent(mod=mod, code="", type=TTkK.SpecialKey, key=key )
else:
self._key = TTkKeyEvent(mod=mod, code="", type=TTkK.Character, key=chr(key) )
def __hash__(self) -> int:
return self._key.__hash__()
@ -70,8 +77,15 @@ class TTkShortcut():
@staticmethod
def processKey(key, focusWidget):
# TTkLog.debug(f"{str(key)=}")
# for k in TTkShortcut._shortcuts:
# TTkLog.debug(f"{str(k)=} - {key==k=}")
if key in TTkShortcut._shortcuts:
for sc in TTkShortcut._shortcuts[key]:
# if sc._parent:
# TTkLog.debug(f"{focusWidget=} {sc._parent=} {sc._parent._parent=}")
# else:
# TTkLog.debug(f"{focusWidget=} {sc._parent=}")
if ( ( sc._shortcutContext == TTkK.WidgetShortcut
and focusWidget == sc._parent )
or ( sc._shortcutContext == TTkK.WidgetWithChildrenShortcut

17
TermTk/TTkCore/string.py

@ -373,6 +373,23 @@ class TTkString():
return ret
def extractShortcuts(self):
def _chGenerator():
for ch,color in zip(self._text,self._colors):
yield ch,color
_newText = ""
_newColors = []
_ret = []
_gen = _chGenerator()
for ch,color in _gen:
if ch == '&':
ch,color = next(_gen)
_ret.append(ch)
color += TTkColor.UNDERLINE
_newText += ch
_newColors.append(color)
return TTkString._importString1(_newText,_newColors), _ret
def replace(self, *args, **kwargs):
''' **replace** (*old*, *new*, *count*)

21
TermTk/TTkWidgets/menu.py

@ -74,12 +74,6 @@ class TTkMenuButton(TTkWidget):
self._checkable = checkable
self._shortcuts = []
self._highlighted = False
while self._text.find('&') != -1:
index = self.text().find('&')
shortcut = self.text().charAt(index+1)
TTkHelper.addShortcut(self, shortcut)
self._shortcuts.append(index)
self.setText(self.text().substring(to=index)+self.text().substring(fr=index+1))
super().__init__(**kwargs)
width = self._text.termWidth() + (3 if self._checkable else 1)
self.setMinimumWidth(width)
@ -161,6 +155,7 @@ class TTkMenuButton(TTkWidget):
self.textChanged.emit(self._text)
self.update()
@pyTTkSlot()
def shortcutEvent(self):
self._triggerButton()
@ -213,7 +208,10 @@ class TTkMenuButton(TTkWidget):
def addMenu(self, text:TTkString, data:object=None, checkable:bool=False, checked:bool=False):
'''addMenu'''
text = text if issubclass(type(text),TTkString) else TTkString(text)
text, shortcuts = text.extractShortcuts()
button = TTkMenuButton(text=text, data=data, checkable=checkable, checked=checked)
button._shortcuts = shortcuts
self._submenu.append(button)
return button
@ -235,8 +233,6 @@ class TTkMenuButton(TTkWidget):
if self._submenu:
canvas._set(0, w-1, '', style['color'])
off = 0
for i in self._shortcuts:
canvas._set(0,i+off, self._text.charAt(i), TTkColor.UNDERLINE)
class _TTkMenuAreaWidget(TTkAbstractScrollView):
__slots__ = ('_submenu','_minWith','_caller')
@ -269,9 +265,9 @@ class _TTkMenuAreaWidget(TTkAbstractScrollView):
def keyEvent(self, evt) -> bool:
if not self._submenu: return False
btns = [b for b in self._submenu if type(b)==TTkMenuButton]
if evt.type == TTkK.SpecialKey:
# Retrieve the current highlighted button
btns = [b for b in self._submenu if type(b)==TTkMenuButton]
curBtn = _b[0] if (_b := [b for b in btns if b._highlighted]) else None
if evt.key == TTkK.Key_Up:
self._cleanHighlight()
@ -300,6 +296,13 @@ class _TTkMenuAreaWidget(TTkAbstractScrollView):
if curBtn:
curBtn._triggerSubmenu()
return True
else:
# Handle shortcuts
ch = evt.key
for btn in btns:
if ch in btn._shortcuts:
btn.shortcutEvent()
return True
return super().keyEvent(evt)
def resizeEvent(self, w, h):

15
TermTk/TTkWidgets/menubar.py

@ -28,6 +28,7 @@ from TermTk.TTkCore.color import TTkColor
# from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.shortcut import TTkShortcut
from TermTk.TTkLayouts.layout import TTkLayout
from TermTk.TTkLayouts.boxlayout import TTkHBoxLayout
from TermTk.TTkWidgets.menu import TTkMenuButton
@ -41,14 +42,7 @@ class TTkMenuBarButton(TTkMenuButton):
__slots__=('_shortcut')
def __init__(self, *, text=..., data=None, checkable=False, checked=False, **kwargs):
self._shortcut = []
super().__init__(text=text, data=data, checkable=checkable, checked=checked, **kwargs)
while self.text().find('&') != -1:
index = self.text().find('&')
shortcut = self.text().charAt(index+1)
TTkHelper.addShortcut(self, shortcut)
self._shortcut.append(index)
self.setText(self.text().substring(to=index)+self.text().substring(fr=index+1))
txtlen = self.text().termWidth()
super().__init__(text=text, data=data, checkable=checkable, checked=checked, shortcutPrefix=TTkK.ALT, **kwargs)
self.setCheckable(self.isCheckable())
def setCheckable(self, ch):
@ -101,7 +95,12 @@ class TTkMenuBarLayout(TTkHBoxLayout):
def addMenu(self,text:TTkString, data:object=None, checkable:bool=False, checked:bool=False, alignment=TTkK.LEFT_ALIGN):
'''addMenu'''
text = text if issubclass(type(text),TTkString) else TTkString(text)
text, shortcuts = text.extractShortcuts()
button = TTkMenuBarButton(text=text, data=data, checkable=checkable, checked=checked)
for ch in shortcuts:
shortcut = TTkShortcut(key=TTkK.ALT | ord(ch.upper()))
shortcut.activated.connect(button.shortcutEvent)
self._mbItems(alignment).addWidget(button)
self._buttons.append(button)
self.update()

101
tests/t.ui/test.ui.031.shortcut.02.menu.py

@ -0,0 +1,101 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2023 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys, os
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
class WindowThatHandleKeypress(ttk.TTkWindow):
def keyEvent(self, evt) -> bool:
if evt.mod==ttk.TTkK.ControlModifier:
if evt.key == ttk.TTkK.Key_F:
ttk.TTkLog.debug("Pressed Key CTRL F inside the Window")
return True
return super().keyEvent(evt)
def setMenu(curMenu:ttk.TTkMenuButton):
curMenu.addMenu("New File")
curMenu.addMenu("Old File")
curMenu.addSpacer()
curMenu.addMenu("Open",checkable=True)
curMenu.addMenu("Save",checkable=True,checked=True)
curMenu.addMenu("Save as").setDisabled()
curMenu.addSpacer()
exportFileMenu = curMenu.addMenu("E&xport")
txtExportFileMenu = exportFileMenu.addMenu("t&xt")
txtExportFileMenu.addMenu("ASCII")
txtExportFileMenu.addMenu("URF-8")
txtExportFileMenu.addMenu("PETSCII")
exportFileMenu = curMenu.addMenu("E&xport 2")
txtExportFileMenu = exportFileMenu.addMenu("t&xt 2")
txtExportFileMenu.addMenu("ASCII 2")
txtExportFileMenu.addMenu("URF-8 2")
txtExportFileMenu.addMenu("PETSCII 2")
exportFileMenu.addMenu("&json")
exportFileMenu.addMenu("&yaml")
curMenu.addSpacer()
curMenu.addMenu("Closeeeeeeeee1234567890")
curMenu.addMenu("Close")
curMenu.addSpacer()
curMenu.addMenu("Exit")
root = ttk.TTk(mouseTrack=True)
window = ttk.TTkWindow(title="Test MenuBar", parent=root,pos=(30,1), size=(60,10), border=True)
menuTop = ttk.TTkMenuBarLayout()
setMenu(menuTop.addMenu("&File"))
window.setMenuBar(menuTop)
#menuBottom = ttk.TTkMenuBarLayout()
#setMenu(menuBottom.addMenu("&Fi&le"))
#window.setMenuBar(menuBottom, ttk.TTkK.BOTTOM)
window = WindowThatHandleKeypress(title="Handle CTRL F if focused", parent=root, pos=(0,5), size=(40,10))
#menuTop = ttk.TTkMenuBarLayout()
#setMenu(menuTop.addMenu("&File"))
#window.setMenuBar(menuTop)
#menuBottom = ttk.TTkMenuBarLayout()
#setMenu(menuBottom.addMenu("&Fi&le"))
#window.setMenuBar(menuBottom, ttk.TTkK.BOTTOM)
logWin = ttk.TTkWindow(title="LOG", parent=root, pos=(20,10), size=(100,20), border=True, layout=ttk.TTkGridLayout())
ttk.TTkLogViewer(parent=logWin)
WindowThatHandleKeypress(title="Handle CTRL F if focused", parent=root, pos=(0,15), size=(40,10))
sc = ttk.TTkShortcut(ttk.TTkK.ALT | ttk.TTkK.Key_A)
sc.activated.connect(lambda : ttk.TTkLog.debug("Pressed Key Alt A"))
sc = ttk.TTkShortcut(ttk.TTkK.ALT | ttk.TTkK.Key_B)
sc.activated.connect(lambda : ttk.TTkLog.debug("Pressed Key Alt B"))
sc = ttk.TTkShortcut(ttk.TTkK.CTRL | ttk.TTkK.Key_F)
sc.activated.connect(lambda : ttk.TTkLog.debug("Pressed Key CTRL F"))
sc = ttk.TTkShortcut(ttk.TTkK.CTRL | ttk.TTkK.ALT | ttk.TTkK.Key_F)
sc.activated.connect(lambda : ttk.TTkLog.debug("Pressed Key CTRL ALT F"))
sc = ttk.TTkShortcut(ttk.TTkK.CTRL | ttk.TTkK.ALT | ttk.TTkK.SHIFT | ttk.TTkK.Key_D) # it depend on the terminal used
sc.activated.connect(lambda : ttk.TTkLog.debug("Pressed Key CTRL ALT SHIFT D")) # it depend if the terminal allows it
root.mainloop()
Loading…
Cancel
Save