diff --git a/pysollib/app.py b/pysollib/app.py index 52941829..5f1b79c6 100644 --- a/pysollib/app.py +++ b/pysollib/app.py @@ -430,6 +430,16 @@ class Application: self.toolbar.config( 'shuffle', self.opt.toolbar_vars['shuffle'] and self.game.canShuffle()) + if TOOLKIT == 'kivy': + self.toolbar.config( + 'undo', + self.opt.toolbar_vars['undo'] and self.game.canUndo()) + self.toolbar.config( + 'undo', + self.opt.toolbar_vars['redo'] and self.game.canRedo()) + self.toolbar.config( + 'autodrop', + self.opt.toolbar_vars['autodrop'] and not self.game.canShuffle()) # noqa # delete intro progress bar if self.intro.progress: self.intro.progress.destroy() diff --git a/pysollib/kivy/LApp.py b/pysollib/kivy/LApp.py index 6edc0c5e..a2a94e99 100644 --- a/pysollib/kivy/LApp.py +++ b/pysollib/kivy/LApp.py @@ -44,6 +44,7 @@ from kivy.uix.actionbar import ActionPrevious from kivy.uix.actionbar import ActionView from kivy.uix.behaviors import ButtonBehavior from kivy.uix.boxlayout import BoxLayout +from kivy.uix.floatlayout import FloatLayout from kivy.uix.image import Image as KivyImage from kivy.uix.label import Label from kivy.uix.scrollview import ScrollView @@ -1814,7 +1815,9 @@ class LApp(App): # Config.set('input', 'multitouchscreen1', 'tuio,0.0.0.0:3333') + self.baseWindow = FloatLayout() # needed e.g. for toasts self.mainWindow = LMainWindow() + self.baseWindow.add_widget(self.mainWindow) logging.info('top = %s' % str(self.mainWindow)) Cache.register('LAppCache', limit=10) Cache.append('LAppCache', 'mainWindow', self.mainWindow, timeout=0) diff --git a/pysollib/kivy/toast.py b/pysollib/kivy/toast.py new file mode 100644 index 00000000..685002fc --- /dev/null +++ b/pysollib/kivy/toast.py @@ -0,0 +1,68 @@ +# ================================================================ +# flake8: noqa +# Toast implementation +# LB230919 + +from kivy.animation import Animation +from kivy.clock import Clock +from kivy.uix.label import Label +from kivy.graphics import Color +from kivy.graphics.vertex_instructions import RoundedRectangle + +# ================================================================ + +class Toast(Label): + def __init__(self, **kw): + super().__init__(opacity=0, **kw) + + self.duration = 4.0 + self.tsize = self.size + self.rsize = 20 + with self.canvas.before: + Color(0.2, 0.2, 0.2, 0.8) + self.rect = RoundedRectangle() + self.bind(size=self._update_rect) + self.bind(texture_size=self.eval_size) + + def eval_size(self,instance,size): + width, height = size + if width > self.parent.width: + instance.text_size = (self.parent.width, None) + instance.texture_update() + width, height = instance.texture_size + ads = height * 1.7 + self.tsize = (width + ads, height + ads) + self.rsize = [(ads+height)/2.0,] + #print(self.tsize,self.rsize) + + def _update_rect(self, instance, value): + self.rect.size = self.tsize + self.rect.pos = (instance.center_x-self.tsize[0]/2.0,instance.center_y-self.tsize[1]/2.0) + self.rect.radius = self.rsize + + def stop(self, *args): + self.parent.remove_widget(self) + + def hide(self, *args): + anim = Animation(opacity=0, duration=0.4) + anim.bind(on_complete=self.stop) + anim.start(self) + + # Timed display with fadein/-out + def show(self, parent=None, duration=2.0): + if parent is None: + return + self.duration = duration + parent.add_widget(self) + anim = Animation(opacity=1, duration=0.4) + anim.start(self) + Clock.schedule_once(self.hide,self.duration) + + # Popup display - use 'stop' to terminate. + def start(self,parent=None): + if parent is None: + return + self.opacity = 1 + parent.add_widget(self) + +# ================================================================ diff --git a/pysollib/kivy/toolbar.py b/pysollib/kivy/toolbar.py index 01d5bd83..d170a471 100644 --- a/pysollib/kivy/toolbar.py +++ b/pysollib/kivy/toolbar.py @@ -20,10 +20,10 @@ # imports import os +from time import time # PySol imports from pysollib.mygettext import _, n_ -from pysollib.settings import TITLE from pysollib.util import IMAGE_EXTENSIONS from pysollib.winsystems import TkSettings @@ -31,64 +31,36 @@ from pysollib.winsystems import TkSettings # * # ************************************************************************ - -class AbstractToolbarButton: - def __init__(self, parent, toolbar, toolbar_name, position): - self.toolbar = toolbar - self.toolbar_name = toolbar_name - self.position = position - self.visible = False - - def show(self, orient, force=False): - if self.visible and not force: - return - self.visible = True - padx, pady = 2, 2 - if orient == 'horizontal': - self.grid(row=0, - column=self.position, - ipadx=padx, ipady=pady, - sticky='nsew') - else: - self.grid(row=self.position, - column=0, - ipadx=padx, ipady=pady, - sticky='nsew') - - def hide(self): - if not self.visible: - return - self.visible = False - self.grid_forget() +from pysollib.kivy.LApp import LImage +from pysollib.kivy.LApp import LBase +from pysollib.kivy.toast import Toast +# from LApp import LMainWindow +from kivy.uix.boxlayout import BoxLayout +# from kivy.uix.button import Button +from kivy.uix.behaviors import ButtonBehavior +# from kivy.uix.behaviors import ToggleButtonBehavior +from kivy.uix.image import Image as KivyImage # ************************************************************************ - -if True: - from pysollib.kivy.LApp import LImage - from pysollib.kivy.LApp import LBase - # from LApp import LMainWindow - from kivy.uix.boxlayout import BoxLayout - # from kivy.uix.button import Button - from kivy.uix.behaviors import ButtonBehavior - # from kivy.uix.behaviors import ToggleButtonBehavior - from kivy.uix.image import Image as KivyImage - -# ************************************************************************ +from kivy.cache import Cache class MyButton(ButtonBehavior, KivyImage, LBase): def __init__(self, **kwargs): super(MyButton, self).__init__(**kwargs) - # super(MyButton, self).__init__() self.src = None if ('image' in kwargs): self.src = kwargs['image'].source self.command = None if ('command' in kwargs): self.command = kwargs['command'] + self.name = "" + if ('name' in kwargs): + self.name = kwargs['name'] self.source = self.src self.allow_stretch = True + self.shown = True def on_press(self): self.allow_stretch = False @@ -102,13 +74,15 @@ class MyButton(ButtonBehavior, KivyImage, LBase): class MyCheckButton(ButtonBehavior, KivyImage, LBase): def __init__(self, **kwargs): super(MyCheckButton, self).__init__(**kwargs) - # super(MyCheckButton, self).__init__() self.src = None if ('image' in kwargs): self.src = kwargs['image'].source self.command = None if ('command' in kwargs): self.command = kwargs['command'] + self.name = "" + if ('name' in kwargs): + self.name = kwargs['name'] self.variable = None if ('variable' in kwargs): self.variable = kwargs['variable'] @@ -118,6 +92,7 @@ class MyCheckButton(ButtonBehavior, KivyImage, LBase): self.source = self.src self.allow_stretch = True self.checked = False + self.shown = True # self.variable = self.win.app.menubar.tkopt.pause if self.variable: @@ -163,6 +138,44 @@ class MyCheckButton(ButtonBehavior, KivyImage, LBase): def on_release(self): pass + +class MyToastButton(ButtonBehavior, KivyImage, LBase): + def __init__(self, **kwargs): + super(MyToastButton, self).__init__(**kwargs) + self.src = None + if ('image' in kwargs): + self.src = kwargs['image'].source + self.command = None + if ('command' in kwargs): + self.command = kwargs['command'] + self.name = "" + if ('name' in kwargs): + self.name = kwargs['name'] + self.timeout = 0.0 + if ('timeout' in kwargs): + self.timeout = kwargs['timeout'] + self.source = self.src + self.allow_stretch = True + self.shown = True + self.start_time = 0.0 + + def on_press(self): + self.allow_stretch = False + self.start_time = time() + + def on_release(self): + self.allow_stretch = True + delta = time()-self.start_time + if (self.command is not None): + if delta > self.timeout: + self.command() + else: + mainApp = Cache.get('LAppCache', 'mainApp') + toast = Toast(text=_("button released too early")) + toast.show(parent=mainApp.baseWindow, duration=2.0) + # print('too early released') + + # ************************************************************************ # * Note: Applications should call show/hide after constructor. # ************************************************************************ @@ -179,15 +192,31 @@ class PysolToolbarTk(BoxLayout): compound='none'): super(PysolToolbarTk, self).__init__(orientation='vertical') - self.size_hint = (0.05, 1.0) + self.size_hint = (0.06, 1.0) # self.size_hint=(None, 1.0) # self.width = 50 self.win = top self.menubar = menubar self.dir = dir self.win.setTool(self, 3) + self.buttons = [] - for label, f, t in ( + # This is called only once after program start. Configurations + # have to take place elsewhere. + + bl = [] + bl.append((n_("New"), self.mNewGame, _("New game"))) + bl.append((n_("Restart"), self.mRestart, _("Restart the\ncurrent game"))) # noqa + bl.append((n_("Undo"), self.mUndo, _("Undo last move"))) # noqa + bl.append((n_("Redo"), self.mRedo, _("Redo last move"))) + bl.append((n_("Autodrop"), self.mDrop, _("Auto drop cards"))) + bl.append((n_("Shuffle"), self.mShuffle, _("Shuffle tiles"))) + bl.append((n_("Hint"), self.mHint, _("Hint"))) + bl.append((n_("Pause"), self.mPause, _("Pause game"))) + bl.append((n_("Rules"), self.mHelpRules, _("Rules for this game"))) + + ''' + for label, f, t in [ (n_("New"), self.mNewGame, _("New game")), (n_("Restart"), self.mRestart, _("Restart the\ncurrent game")), (None, None, None), @@ -205,40 +234,27 @@ class PysolToolbarTk(BoxLayout): (n_("Rules"), self.mHelpRules, _("Rules for this game")), (None, None, None), (n_("Quit"), self.mHoldAndQuit, _("Quit %s") % TITLE), - ): + ]: + ''' + + for label, f, t in bl: if label is None: - # sep = self._createSeparator() - # sep.bind("<1>", self.clickHandler) - # sep.bind("<3>", self.rightclickHandler) + # We dont have separators in kivy version. pass elif label == 'Pause': - self._createButton(label, f, check=True, tooltip=t) + button = self._createButton(label, f, check=True, tooltip=t) + self.buttons.append(button) + elif label in ["New", "Restart"]: + button = self._createButton(label, f, check=False, tooltip=t, timeout=1.0) # noqa + self.buttons.append(button) else: - self._createButton(label, f, tooltip=t) - - # hier gibt es noch ein 'player label' mit contextmenu, wo - # der spielername gewählt und die spielstatistik etc. - # angezeigt werden könnte (TBD): - ''' - sep = self._createFlatSeparator() - sep.bind("<1>", self.clickHandler) - sep.bind("<3>", self.rightclickHandler) - self._createLabel("player", label=n_('Player'), - tooltip=_("Player options")) - # - self.player_label.bind("<1>", self.mOptPlayerOptions) - # self.player_label.bind("<3>", self.mOptPlayerOptions) - self.popup = MfxMenu(master=None, label=n_('Toolbar'), tearoff=0) - createToolbarMenu(menubar, self.popup) - self.frame.bind("<1>", self.clickHandler) - self.frame.bind("<3>", self.rightclickHandler) - # - self.setCompound(compound, force=True) - ''' + button = self._createButton(label, f, check=False, tooltip=t) + self.buttons.append(button) def show(self, on, **kw): side = self.menubar.tkopt.toolbar.get() self.win.setTool(None, side) + print('******** toolbar show', on, side, kw) return False def mHoldAndQuit(self, *args): @@ -253,9 +269,23 @@ class PysolToolbarTk(BoxLayout): pass def config(self, w, v): - print('PysolToolbarTk: config %s, %s' % (w, v)) - # y = self.yy - pass + print('********************* PysolToolbarTk: config %s, %s' % (w, v)) + + # This is the position, where the toolbar can be configured. + + chgd = False + for b in self.buttons: + if b.name == w: + ov = b.shown + if v != ov: + b.shown = v + chgd = True + + if chgd: + self.clear_widgets() + for b in self.buttons: + if b.shown: + self.add_widget(b) # Lokale. @@ -267,11 +297,10 @@ class PysolToolbarTk(BoxLayout): if os.path.isfile(file): image = LImage(source=file) # print('_loadImage: file=%s' % file) - # image = Tkinter.PhotoImage(file=file) break return image - def _createButton(self, label, command, check=False, tooltip=None): + def _createButton(self, label, command, check=False, tooltip=None, timeout=0.0): # noqa name = label.lower() image = self._loadImage(name) # position = len(self._widgets) @@ -289,27 +318,26 @@ class PysolToolbarTk(BoxLayout): 'padx': padx, 'pady': pady, 'overrelief': 'raised', + 'timeout': timeout } # print ('toolbar: print %s' % self.win) # print ('toolbar: print %s' % self.win.app) kw['win'] = self.win if image: kw['image'] = image + if name: + kw['name'] = name if check: kw['offrelief'] = button_relief kw['indicatoron'] = False kw['selectcolor'] = '' button = MyCheckButton(**kw) + elif timeout > 0.0: + button = MyToastButton(**kw) else: button = MyButton(**kw) - # button.show(orient=self.orient) - setattr(self, name + "_image", image) - setattr(self, name + "_button", button) - # self._widgets.append(button) - self.add_widget(button) - # TBD: tooltip ev. auf basis einer statuszeile implementieren # if tooltip: # b = MfxTooltip(button) diff --git a/pysollib/main.py b/pysollib/main.py index 7b8fcf49..a3691014 100644 --- a/pysollib/main.py +++ b/pysollib/main.py @@ -431,11 +431,11 @@ if TOOLKIT == 'kivy': logging.info("KivyApp: build") self.app = app = Application() - app.top = self.mainWindow + app.top = self.baseWindow self.startCode = pysol_init(app, self.args) logging.info('Main: App Initialised - starting main loop') - return self.mainWindow + return self.baseWindow def main(args=None): logging.basicConfig(level=logging.INFO)