1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-15 02:54:09 -04:00

more rework on toolbar and menu options

- toolbar button configurations added to option menu
 - refactoring
 - metadata (fastlane) updated
This commit is contained in:
lufebe16 2023-09-21 12:18:55 +02:00
parent 158c3137fd
commit ae9aa78cde
11 changed files with 194 additions and 93 deletions

View file

@ -63,9 +63,6 @@ Cardsets:
LB230301. LB230301.
(After new changes as of 27.3.23 - not contained in the last f-droid
version):
Allow external storage access for Android 10 and later. Allow external storage access for Android 10 and later.
- on Android 10 it ist still possible to write to the extenal storage. A - on Android 10 it ist still possible to write to the extenal storage. A
@ -83,3 +80,10 @@ LB230301.
never query you for that. You may do it on your own. never query you for that. You may do it on your own.
LB230327. LB230327.
Scheduled for the next Android release:
- Screen rotation lock.
- Toolbar actualised
- Protection from accidental reset or redeal.
LB230919.

View file

@ -173,7 +173,7 @@ mkdir -p "$PKGTREE"
### Alternate toolkit. ### Alternate toolkit.
- Kivy (10.0 or later) - Kivy
- Features: - Features:
- Sound support integrated. - Sound support integrated.
- Android apk build support. - Android apk build support.
@ -189,7 +189,11 @@ On the basis of Kivy an Android App is also available. You may build
your own using appropriate build instructions in README.android and your own using appropriate build instructions in README.android and
in Directory buildozer. in Directory buildozer.
Some versions will also be published on F-droid (https://f-droid.org) Some versions will also be published on F-droid.
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/org.lufebe16.pysolfc/)
### Configuring Freecell Solver ### Configuring Freecell Solver

View file

@ -5,3 +5,7 @@ Please use the new setup presented in directory
buildozer. buildozer.
LB221118. LB221118.
This Directory will soon be deleted from the Repo.
LB230919.

View file

@ -11,8 +11,8 @@
"type": "SINGLE", "type": "SINGLE",
"filters": [], "filters": [],
"attributes": [], "attributes": [],
"versionCode": 102122001, "versionCode": 102122100,
"versionName": "2.20.1", "versionName": "2.21.0",
"outputFile": "pysolfc-release.apk" "outputFile": "pysolfc-release.apk"
} }
], ],

View file

@ -0,0 +1,10 @@
Android specific:
- Temporary screen orientation lock added. Long tap (5 seconds) to the
playground locks/unlocks (toggle) automatic screen rotation.
- Toolbar: configuration of displayed toolbar buttons added to options menu.
- Toolbar: dynamic updates on Toolbar and Options settings.
- Toolbar buttons for 'new deal' and 'restart' have now a delayed
functionality. This prevents from accidental execution.
Main version:
- consult NEWS.asscidoc or html-src/news.html on
github for more change informations.

View file

@ -430,16 +430,6 @@ class Application:
self.toolbar.config( self.toolbar.config(
'shuffle', 'shuffle',
self.opt.toolbar_vars['shuffle'] and self.game.canShuffle()) 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 # delete intro progress bar
if self.intro.progress: if self.intro.progress:
self.intro.progress.destroy() self.intro.progress.destroy()

View file

@ -38,6 +38,7 @@ from kivy.graphics import Color
from kivy.graphics import Line from kivy.graphics import Line
from kivy.graphics import Rectangle from kivy.graphics import Rectangle
from kivy.graphics import Triangle from kivy.graphics import Triangle
from kivy.properties import NumericProperty
from kivy.properties import StringProperty from kivy.properties import StringProperty
from kivy.uix.actionbar import ActionButton from kivy.uix.actionbar import ActionButton
from kivy.uix.actionbar import ActionPrevious from kivy.uix.actionbar import ActionPrevious
@ -52,7 +53,6 @@ from kivy.uix.treeview import TreeView
from kivy.uix.treeview import TreeViewLabel from kivy.uix.treeview import TreeViewLabel
from kivy.uix.widget import Widget from kivy.uix.widget import Widget
from kivy.utils import platform from kivy.utils import platform
from kivy.properties import NumericProperty
from pysollib.kivy.androidperms import requestStoragePerm from pysollib.kivy.androidperms import requestStoragePerm
from pysollib.kivy.androidrot import AndroidScreenRotation from pysollib.kivy.androidrot import AndroidScreenRotation

View file

@ -1112,10 +1112,11 @@ class OptionsMenuDialog(LMenuDialog):
# 'Top', # 'Top',
# self.menubar.tkopt.toolbar, 1, # self.menubar.tkopt.toolbar, 1,
# self.menubar.mOptToolbar) # self.menubar.mOptToolbar)
# self.addRadioNode(tv, rg, # self.addRadioNode(tv, rg,
# 'Bottom', # _('Bottom'),
# self.menubar.tkopt.toolbar, 2, # self.menubar.tkopt.toolbar, 2,
# self.menubar.mOptToolbar) # self.menubar.mOptToolbar)
self.addRadioNode(tv, rg, self.addRadioNode(tv, rg,
_('Left'), _('Left'),
@ -1126,6 +1127,17 @@ class OptionsMenuDialog(LMenuDialog):
self.menubar.tkopt.toolbar, 4, self.menubar.tkopt.toolbar, 4,
self.menubar.mOptToolbar) self.menubar.mOptToolbar)
rg1 = tv.add_node(
LTreeNode(text=_('Buttons:')), rg)
if rg1:
for w in TOOLBAR_BUTTONS:
ww = w
ww[0].upper()
self.addCheckNode(tv, rg,
_(ww), # noqa
self.menubar.tkopt.toolbar_vars[w],
self.make_vars_command(self.menubar.mOptToolbarConfig, w)) # noqa
# ------------------------------------------- # -------------------------------------------
# Statusbar - not implemented # Statusbar - not implemented
@ -2441,7 +2453,6 @@ the next time you restart the %(app)s""") % {'app': TITLE})
if self._cancelDrag(break_pause=False): if self._cancelDrag(break_pause=False):
return return
self.app.opt.toolbar_vars[w] = v self.app.opt.toolbar_vars[w] = v
self.app.toolbar.config(w, v)
self.top.update_idletasks() self.top.update_idletasks()
# #

View file

@ -53,18 +53,19 @@ COMPOUNDS = (
TOOLBAR_BUTTONS = ( TOOLBAR_BUTTONS = (
"new", "new",
"restart", "restart",
"open", # "open",
"save", # "save",
"undo", "undo",
"redo", "redo",
"autodrop", "autodrop",
"shuffle", "shuffle",
"hint",
"pause", "pause",
"statistics", # "statistics",
"rules", "rules",
"quit", # "quit",
"player", # "player",
) )
STATUSBAR_ITEMS = ( STATUSBAR_ITEMS = (
('stuck', "'You Are Stuck' indicator"), ('stuck', "'You Are Stuck' indicator"),

View file

@ -17,12 +17,11 @@ class Toast(Label):
self.duration = 4.0 self.duration = 4.0
self.tsize = self.size self.tsize = self.size
self.rsize = 20 self.rsize = [20,]
with self.canvas.before: with self.canvas.before:
Color(0.2, 0.2, 0.2, 0.8) Color(0.2, 0.2, 0.2, 0.8)
self.rect = RoundedRectangle() self.rect = RoundedRectangle()
self.bind(size=self._update_rect) self.bind(size=self._update_rect)
self.bind(texture_size=self.eval_size)
def eval_size(self,instance,size): def eval_size(self,instance,size):
width, height = size width, height = size
@ -33,14 +32,17 @@ class Toast(Label):
ads = height * 1.7 ads = height * 1.7
self.tsize = (width + ads, height + ads) self.tsize = (width + ads, height + ads)
self.rsize = [(ads+height)/2.0,] self.rsize = [(ads+height)/2.0,]
#print(self.tsize,self.rsize) # print(self.tsize, self.rsize)
def _update_rect(self, instance, value): def _update_rect(self, instance, value):
self.rect.size = self.tsize self.rect.size = self.tsize
self.rect.pos = (instance.center_x-self.tsize[0]/2.0,instance.center_y-self.tsize[1]/2.0) # print(self.top)
# print(self.pos_hint)
self.rect.pos = (self.center_x-self.tsize[0]/2.0,self.center_y-self.tsize[1]/2.0)
self.rect.radius = self.rsize self.rect.radius = self.rsize
def stop(self, *args): def stop(self, *args):
self.unbind(texture_size=self.eval_size)
self.parent.remove_widget(self) self.parent.remove_widget(self)
def hide(self, *args): def hide(self, *args):
@ -54,6 +56,7 @@ class Toast(Label):
return return
self.duration = duration self.duration = duration
parent.add_widget(self) parent.add_widget(self)
self.bind(texture_size=self.eval_size)
anim = Animation(opacity=1, duration=0.4) anim = Animation(opacity=1, duration=0.4)
anim.start(self) anim.start(self)
Clock.schedule_once(self.hide,self.duration) Clock.schedule_once(self.hide,self.duration)
@ -64,5 +67,6 @@ class Toast(Label):
return return
self.opacity = 1 self.opacity = 1
parent.add_widget(self) parent.add_widget(self)
self.bind(texture_size=self.eval_size)
# ================================================================ # ================================================================

View file

@ -22,33 +22,33 @@
import os import os
from time import time from time import time
from kivy.cache import Cache
from kivy.clock import Clock
from kivy.properties import BooleanProperty
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image as KivyImage
# PySol kivy imports
from pysollib.kivy.LApp import LBase
from pysollib.kivy.LApp import LImage
from pysollib.kivy.toast import Toast
# PySol imports # PySol imports
from pysollib.mygettext import _, n_ from pysollib.mygettext import _, n_ # noqa
from pysollib.util import IMAGE_EXTENSIONS from pysollib.util import IMAGE_EXTENSIONS
from pysollib.winsystems import TkSettings from pysollib.winsystems import TkSettings
# ************************************************************************
# *
# ************************************************************************
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
# ************************************************************************ # ************************************************************************
from kivy.cache import Cache class MyButtonBase(ButtonBehavior, KivyImage, LBase):
shown = BooleanProperty(True)
enabled = BooleanProperty(True)
config = BooleanProperty(True)
class MyButton(ButtonBehavior, KivyImage, LBase):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(MyButton, self).__init__(**kwargs) super(MyButtonBase, self).__init__(**kwargs)
self.src = None self.src = None
if ('image' in kwargs): if ('image' in kwargs):
self.src = kwargs['image'].source self.src = kwargs['image'].source
@ -60,7 +60,23 @@ class MyButton(ButtonBehavior, KivyImage, LBase):
self.name = kwargs['name'] self.name = kwargs['name']
self.source = self.src self.source = self.src
self.allow_stretch = True self.allow_stretch = True
self.shown = True
def set_shown(self, instance, value):
# print ('** set shown (',self.name ,') called', value)
self.shown = value
def set_enabled(self, instance, value):
# print ('** set enabled (',self.name ,') called', value)
self.enabled = value
def set_config(self, instance, value):
# print ('** set config (',self.name ,') called', value)
self.config = value
class MyButton(MyButtonBase):
def __init__(self, **kwargs):
super(MyButton, self).__init__(**kwargs)
def on_press(self): def on_press(self):
self.allow_stretch = False self.allow_stretch = False
@ -71,28 +87,16 @@ class MyButton(ButtonBehavior, KivyImage, LBase):
self.command() self.command()
class MyCheckButton(ButtonBehavior, KivyImage, LBase): class MyCheckButton(MyButtonBase):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(MyCheckButton, self).__init__(**kwargs) super(MyCheckButton, 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.variable = None self.variable = None
if ('variable' in kwargs): if ('variable' in kwargs):
self.variable = kwargs['variable'] self.variable = kwargs['variable']
self.win = None self.win = None
if ('win' in kwargs): if ('win' in kwargs):
self.win = kwargs['win'] self.win = kwargs['win']
self.source = self.src
self.allow_stretch = True
self.checked = False self.checked = False
self.shown = True
# self.variable = self.win.app.menubar.tkopt.pause # self.variable = self.win.app.menubar.tkopt.pause
if self.variable: if self.variable:
@ -139,24 +143,12 @@ class MyCheckButton(ButtonBehavior, KivyImage, LBase):
pass pass
class MyToastButton(ButtonBehavior, KivyImage, LBase): class MyToastButton(MyButtonBase):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(MyToastButton, self).__init__(**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 self.timeout = 0.0
if ('timeout' in kwargs): if ('timeout' in kwargs):
self.timeout = kwargs['timeout'] self.timeout = kwargs['timeout']
self.source = self.src
self.allow_stretch = True
self.shown = True
self.start_time = 0.0 self.start_time = 0.0
def on_press(self): def on_press(self):
@ -172,10 +164,46 @@ class MyToastButton(ButtonBehavior, KivyImage, LBase):
else: else:
mainApp = Cache.get('LAppCache', 'mainApp') mainApp = Cache.get('LAppCache', 'mainApp')
toast = Toast(text=_("button released too early")) toast = Toast(text=_("button released too early"))
# toast = Toast(text=_("button released too early"),pos_hint={'top': 0.8}) # noqa
# pos hint wirkt nur auf den text, nicht auf die box !!!
toast.show(parent=mainApp.baseWindow, duration=2.0) toast.show(parent=mainApp.baseWindow, duration=2.0)
# print('too early released') # print('too early released')
class MyWaitButton(MyButtonBase):
def __init__(self, **kwargs):
super(MyWaitButton, self).__init__(**kwargs)
self.timeout = 0.0
if ('timeout' in kwargs):
self.timeout = kwargs['timeout']
self.start_time = 0.0
self.eventId = None
self.wait_toast = None
def time_out(self, *args):
self.wait_toast.stop()
self.wait_toast = None
self.eventId = None
if (self.command is not None):
self.command()
# print ('timeout')
def on_press(self):
self.allow_stretch = False
self.eventId = Clock.schedule_once(self.time_out, 1.0)
mainApp = Cache.get('LAppCache', 'mainApp')
self.wait_toast = Toast(text=_("hold on ..."))
self.wait_toast.start(mainApp.baseWindow)
def on_release(self):
self.allow_stretch = True
if self.eventId is not None:
Clock.unschedule(self.eventId)
# print ('unscheduled')
if self.wait_toast is not None:
self.wait_toast.stop()
self.wait_toast = None
# ************************************************************************ # ************************************************************************
# * Note: Applications should call show/hide after constructor. # * Note: Applications should call show/hide after constructor.
# ************************************************************************ # ************************************************************************
@ -200,6 +228,7 @@ class PysolToolbarTk(BoxLayout):
self.dir = dir self.dir = dir
self.win.setTool(self, 3) self.win.setTool(self, 3)
self.buttons = [] self.buttons = []
self.buttond = {}
# This is called only once after program start. Configurations # This is called only once after program start. Configurations
# have to take place elsewhere. # have to take place elsewhere.
@ -237,8 +266,11 @@ class PysolToolbarTk(BoxLayout):
]: ]:
''' '''
# Build all the buttions.
for label, f, t in bl: for label, f, t in bl:
if label is None: if label is None:
button = None
# We dont have separators in kivy version. # We dont have separators in kivy version.
pass pass
elif label == 'Pause': elif label == 'Pause':
@ -251,6 +283,23 @@ class PysolToolbarTk(BoxLayout):
button = self._createButton(label, f, check=False, tooltip=t) button = self._createButton(label, f, check=False, tooltip=t)
self.buttons.append(button) self.buttons.append(button)
if button is not None:
# print('button name: ', button.name)
self.buttond[button.name] = button
# check buttons if configurated or opted out.
# (could ev. be integrated into _createButton)
toolbar_opt = getattr(self.menubar.tkopt, 'toolbar_vars')
for k in toolbar_opt.keys():
opt = toolbar_opt[k]
if k in self.buttond.keys():
b = self.buttond[k]
b.config = opt.get()
opt.bind(value=b.set_config)
self.redraw()
def show(self, on, **kw): def show(self, on, **kw):
side = self.menubar.tkopt.toolbar.get() side = self.menubar.tkopt.toolbar.get()
self.win.setTool(None, side) self.win.setTool(None, side)
@ -268,24 +317,22 @@ class PysolToolbarTk(BoxLayout):
def updateText(self, **kw): def updateText(self, **kw):
pass pass
def redraw(self):
self.clear_widgets()
for b in self.buttons:
# print(b.name,b.config,b.shown,b.enabled)
if b.shown and b.enabled and b.config:
self.add_widget(b)
def changed_state(self, instance, value):
self.redraw()
def config(self, w, v): def config(self, w, v):
print('********************* PysolToolbarTk: config %s, %s' % (w, v)) print('********************* PysolToolbarTk: config %s, %s' % (w, v))
# This is the position, where the toolbar can be configured. if w == 'shuffle':
self.buttond['shuffle'].shown = v
chgd = False self.buttond['autodrop'].shown = not v
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. # Lokale.
@ -334,10 +381,36 @@ class PysolToolbarTk(BoxLayout):
button = MyCheckButton(**kw) button = MyCheckButton(**kw)
elif timeout > 0.0: elif timeout > 0.0:
button = MyToastButton(**kw) # button = MyToastButton(**kw)
button = MyWaitButton(**kw)
else: else:
button = MyButton(**kw) button = MyButton(**kw)
try:
oname = name
# redo is handled same way as undo, there is no separate option.
if name == 'redo':
oname = 'undo'
opt = getattr(self.menubar.tkopt, oname)
# specialisation (differently used options):
# - autodrop button has no own option. option 'autodrop' is used
# for the different effect of fully automatic dropping!
# - pause button sets and clears the pause option, not vice versa!
# it is an option that only exists internaly (not saved). (same
# applies also to the tk and tile implementations)
if oname not in ['autodrop', 'pause']:
button.enabled = opt.get()
# print('** ', oname, '(enabled) = ', opt.get())
opt.bind(value=button.set_enabled)
button.bind(enabled=self.changed_state)
button.bind(shown=self.changed_state)
except: # noqa
pass
button.bind(config=self.changed_state)
# TBD: tooltip ev. auf basis einer statuszeile implementieren # TBD: tooltip ev. auf basis einer statuszeile implementieren
# if tooltip: # if tooltip:
# b = MfxTooltip(button) # b = MfxTooltip(button)