diff --git a/Makefile b/Makefile index ac6fbdbf..d5faff06 100644 --- a/Makefile +++ b/Makefile @@ -97,5 +97,5 @@ test: .venv flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude .venv,build,tmp ; \ pytest tests/pytest/test_003_string.py ; \ pytest tests/pytest/test_002_textedit.py ; \ - pytest tests/pytest/test_001_demo.py ; + pytest -v tests/pytest/test_001_demo.py ; diff --git a/TermTk/TTkCore/canvas.py b/TermTk/TTkCore/canvas.py index 3172906a..a0b45667 100644 --- a/TermTk/TTkCore/canvas.py +++ b/TermTk/TTkCore/canvas.py @@ -192,10 +192,10 @@ class TTkCanvas: else: self._colors[y][x+i] = colors[i].mod(x+i,y) # Check the full wide chars on the edge of the two canvasses - if self._data[y][x+a] == '': + if ((0 <= (x+a) < self._width) and self._data[y][x+a] == ''): self._data[y][x+a] = TTkCfg.theme.unicodeWideOverflowCh[0] self._colors[y][x+a] = TTkCfg.theme.unicodeWideOverflowColor - if TTkString._isWideCharData(self._data[y][x+b-1]): + if ((0 <= (x+b-1) < self._width) and TTkString._isWideCharData(self._data[y][x+b-1])): self._data[y][x+b-1] = TTkCfg.theme.unicodeWideOverflowCh[1] self._colors[y][x+b-1] = TTkCfg.theme.unicodeWideOverflowColor @@ -598,15 +598,10 @@ class TTkCanvas: w = min(w,self._width-x) h = min(h,self._height-y) - # if x>=self._width: x=self._width-1 - # if y>=self._height: y=self._height-1 - # if w>=self._width-x: w=self._width-x - # if h>=self._height-y: h=self._height-y - - xoffset = 0 if x>=bx else bx-x - yoffset = 0 if y>=by else by-y - wslice = w if x+w < bx+bw else bx+bw-x - hslice = h if y+h < by+bh else by+bh-y + xoffset = min(max(0,bx-x),canvas._width-1) + yoffset = min(max(0,by-y),canvas._height-1) + wslice = min(w if x+w < bx+bw else bx+bw-x,canvas._width) + hslice = min(h if y+h < by+bh else by+bh-y,canvas._height) for iy in range(yoffset,hslice): a, b = x+xoffset, x+wslice @@ -614,16 +609,16 @@ class TTkCanvas: self._colors[y+iy][a:b] = canvas._colors[iy][xoffset:wslice] # Check the full wide chars on the edge of the two canvasses - if self._data[y+iy][a]=='': + if ((0 <= a < self._width) and self._data[y+iy][a]==''): self._data[y+iy][a] = TTkCfg.theme.unicodeWideOverflowCh[0] self._colors[y+iy][a] = TTkCfg.theme.unicodeWideOverflowColor - if TTkString._isWideCharData(self._data[y+iy][b-1]): + if ((0 < b <= self._width) and TTkString._isWideCharData(self._data[y+iy][b-1])): self._data[y+iy][b-1] = TTkCfg.theme.unicodeWideOverflowCh[1] self._colors[y+iy][b-1] = TTkCfg.theme.unicodeWideOverflowColor - if TTkString._isWideCharData(self._data[y+iy][a-1]): + if ((0 < a <= self._width) and TTkString._isWideCharData(self._data[y+iy][a-1])): self._data[y+iy][a-1] = TTkCfg.theme.unicodeWideOverflowCh[1] self._colors[y+iy][a-1] = TTkCfg.theme.unicodeWideOverflowColor - if ( b +# +# 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],'../../tmp')) +sys.path.append(os.path.join(sys.path[0],'../..')) +import TermTk as ttk + +# Testing Window with a checkbox to enable/disable any control button +class WindowFlagsTest(ttk.TTkWindow): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + rb = ttk.TTkCheckbox( + parent=self, pos=(0,0), size=(20,1), text='Reduce Button', + checked=bool(self.windowFlag()&ttk.TTkK.WindowFlag.WindowReduceButtonHint)) + minb = ttk.TTkCheckbox( + parent=self, pos=(0,1), size=(20,1), text='Minimize Button', + checked=bool(self.windowFlag()&ttk.TTkK.WindowFlag.WindowMinimizeButtonHint)) + maxb = ttk.TTkCheckbox( + parent=self, pos=(0,2), size=(20,1), text='Maximize Button', + checked=bool(self.windowFlag()&ttk.TTkK.WindowFlag.WindowMaximizeButtonHint)) + cb = ttk.TTkCheckbox( + parent=self, pos=(0,3), size=(20,1), text='Close Button', + checked=bool(self.windowFlag()&ttk.TTkK.WindowFlag.WindowCloseButtonHint)) + + # Set the window flag/field based on the checkbox state + def _cbStateChanged(state,field): + if state==ttk.TTkK.Checked: + self.setWindowFlag(self.windowFlag()|field) + else: + self.setWindowFlag(self.windowFlag()&(~field)) + + rb.stateChanged.connect( lambda x: _cbStateChanged(x,ttk.TTkK.WindowFlag.WindowReduceButtonHint)) + minb.stateChanged.connect(lambda x: _cbStateChanged(x,ttk.TTkK.WindowFlag.WindowMinimizeButtonHint)) + maxb.stateChanged.connect(lambda x: _cbStateChanged(x,ttk.TTkK.WindowFlag.WindowMaximizeButtonHint)) + cb.stateChanged.connect( lambda x: _cbStateChanged(x,ttk.TTkK.WindowFlag.WindowCloseButtonHint)) + + + +def demoWindowsFlags(root=None): + frame = ttk.TTkFrame(parent=root, border=False) + + # Standard window (the close button is enabled by default) + WindowFlagsTest(parent=frame, pos = (0,0), size=(40,8), title="Test Window 1") + # Enable Max anc Close button + WindowFlagsTest(parent=frame, pos = (2,2), size=(40,8), title="Test Window 2", + flags = ttk.TTkK.WindowFlag.WindowMaximizeButtonHint | ttk.TTkK.WindowFlag.WindowCloseButtonHint) + # Disable all the control buttons + WindowFlagsTest(parent=frame, pos = (4,4), size=(40,8), title="Test Window 3", + flags = ttk.TTkK.NONE) + # Enable only the Max and Min Buttons + WindowFlagsTest(parent=frame, pos = (6,6), size=(40,8), title="Test Window 4", + flags = ttk.TTkK.WindowFlag.WindowMinMaxButtonsHint) + # Enable only the Minimize button + WindowFlagsTest(parent=frame, pos = (8,8), size=(40,8), title="Test Window 5", + flags = ttk.TTkK.WindowFlag.WindowReduceButtonHint) + + return frame + + + +def main(): + ttk.TTkLog.use_default_file_logging() + root = ttk.TTk() + win1 = ttk.TTkWindow(parent=root,pos = (1,1), size=(60,30), title="Test Window Flags", border=True, layout=ttk.TTkGridLayout(), flags=ttk.TTkK.NONE) + demoWindowsFlags(win1) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tests/pytest/test_001_demo.py b/tests/pytest/test_001_demo.py index d5428f06..37b99768 100755 --- a/tests/pytest/test_001_demo.py +++ b/tests/pytest/test_001_demo.py @@ -153,7 +153,18 @@ def test_demo(): assert demo.demoShowcase(root) != None root.quit() +def message_handler(mode, context, message): + msgType = "NONE" + if mode == demo.ttk.TTkLog.InfoMsg: msgType = "[INFO]" + elif mode == demo.ttk.TTkLog.WarningMsg: msgType = "[WARNING]" + elif mode == demo.ttk.TTkLog.CriticalMsg: msgType = "[CRITICAL]" + elif mode == demo.ttk.TTkLog.FatalMsg: msgType = "[FATAL]" + elif mode == demo.ttk.TTkLog.ErrorMsg: msgType = "[ERROR]" + print(f"{msgType} {context.file} {message}") + def test_recording1(): + # demo.ttk.TTkLog.use_default_file_logging() + demo.ttk.TTkLog.installMessageHandler(message_handler) root = TTkRecord(title="pyTermTk Demo Record", record=False) root.loadQueue(open('tmp/test.input.bin', 'rb')) winTabbed1 = demo.ttk.TTkWindow(parent=root,pos=(0,0), size=(80,24), title="pyTermTk Showcase", border=True, layout=demo.ttk.TTkGridLayout())