mirror of
https://github.com/shlomif/PySolFC.git
synced 2025-04-05 00:02:29 -04:00
1916 lines
59 KiB
Python
1916 lines
59 KiB
Python
#!/usr/bin/python
|
|
# -*- mode: python; coding: utf-8; -*-
|
|
# ---------------------------------------------------------------------------#
|
|
#
|
|
# Copyright (C) 2017 LB
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
# ---------------------------------------------------------------------------#
|
|
|
|
from __future__ import division
|
|
|
|
import logging
|
|
import math
|
|
import traceback
|
|
|
|
from kivy.animation import Animation
|
|
from kivy.app import App
|
|
from kivy.base import EventLoop
|
|
from kivy.base import stopTouchApp
|
|
from kivy.cache import Cache
|
|
from kivy.clock import Clock
|
|
from kivy.config import Config
|
|
from kivy.core.audio import SoundLoader
|
|
from kivy.core.window import Window
|
|
from kivy.graphics import Color
|
|
from kivy.graphics import Line
|
|
from kivy.graphics import Rectangle
|
|
from kivy.graphics import Triangle
|
|
from kivy.properties import NumericProperty
|
|
from kivy.properties import StringProperty
|
|
from kivy.uix.actionbar import ActionButton
|
|
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.label import Label
|
|
from kivy.uix.scrollview import ScrollView
|
|
from kivy.uix.treeview import TreeView
|
|
from kivy.uix.treeview import TreeViewLabel
|
|
from kivy.uix.widget import Widget
|
|
from kivy.utils import platform
|
|
|
|
from pysollib.kivy.LBase import LBase
|
|
from pysollib.kivy.androidperms import requestStoragePerm
|
|
from pysollib.kivy.androidrot import AndroidScreenRotation
|
|
from pysollib.resource import CSI
|
|
|
|
if platform != 'android':
|
|
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
|
|
|
|
# =============================================================================
|
|
|
|
|
|
def get_platform():
|
|
return platform
|
|
|
|
# =============================================================================
|
|
|
|
|
|
def get_screen_ori():
|
|
if get_platform() == 'android':
|
|
from jnius import autoclass
|
|
from jnius import cast
|
|
else:
|
|
logging.info("LApp: ori = unknown")
|
|
return None
|
|
|
|
PythonActivity = autoclass('org.kivy.android.PythonActivity')
|
|
currentActivity = cast('android.app.Activity', PythonActivity.mActivity)
|
|
|
|
# Display = autoclass('android.view.Display')
|
|
# WindowManager = autoclass('android.view.WindowManager')
|
|
|
|
wm = currentActivity.getWindowManager()
|
|
d = wm.getDefaultDisplay()
|
|
|
|
so = None
|
|
if d.getWidth() > d.getHeight():
|
|
so = 'landscape'
|
|
else:
|
|
so = 'portrait'
|
|
|
|
logging.info("LApp: ori = %s" % so)
|
|
return so
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LPopCommander(LBase):
|
|
def __init__(self, **kw):
|
|
super(LPopCommander, self).__init__()
|
|
self.pop_command = kw['pop_command']
|
|
|
|
def pop(self):
|
|
if self.pop_command is not None:
|
|
self.pop_command(0)
|
|
return True
|
|
return False
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LAnimationMgr(object):
|
|
def __init__(self, **kw):
|
|
super(LAnimationMgr, self).__init__()
|
|
self.animations = []
|
|
self.widgets = {}
|
|
|
|
def animEnd(self, anim, widget):
|
|
# print('LAnimationMgr: animEnd = %s.%s' % (anim, widget))
|
|
|
|
self.widgets[widget] = self.widgets[widget][1:]
|
|
self.animations.remove(anim)
|
|
if len(self.widgets[widget]) > 0:
|
|
# start next animation on widget
|
|
nanim = self.widgets[widget][0]
|
|
self.animations.append(nanim)
|
|
print('LAnimationMgr: animEnd, append = %s' % (nanim))
|
|
nanim.start(widget)
|
|
else:
|
|
# no further animations for widget so stop
|
|
del self.widgets[widget]
|
|
|
|
# print('Clock.get_fps() ->', Clock.get_fps())
|
|
|
|
def makeAnimStart(self, anim, spos, widget):
|
|
def animStart(dt):
|
|
widget.pos = spos
|
|
# print('LAnimationMgr: animStart = %s ... %s' % (anim, dt))
|
|
anim.start(widget)
|
|
return animStart
|
|
|
|
def checkRunning(self):
|
|
return len(self.animations) > 0
|
|
|
|
def create(self, spos, widget, **kw):
|
|
x = 0.0
|
|
y = 0.0
|
|
duration = 0.2
|
|
transition = 'in_out_quad'
|
|
if 'x' in kw:
|
|
x = kw['x']
|
|
if 'y' in kw:
|
|
y = kw['y']
|
|
if 'duration' in kw:
|
|
duration = kw['duration']
|
|
if 'transition' in kw:
|
|
transition = kw['transition']
|
|
|
|
anim = Animation(x=x, y=y, duration=duration, transition=transition)
|
|
anim.bind(on_complete=self.animEnd)
|
|
if 'bindE' in kw:
|
|
anim.bind(on_complete=kw['bindE'])
|
|
if 'bindS' in kw:
|
|
anim.bind(on_start=kw['bindS'])
|
|
|
|
offset = duration / 3.0
|
|
# offset = duration*1.2
|
|
timedelay = offset * len(self.animations)
|
|
# print('offset = %s'% offset)
|
|
# print('LAnimationMgr: timedelay = %s' % timedelay)
|
|
|
|
if widget in self.widgets:
|
|
# append additional animation to widget
|
|
self.widgets[widget].append(anim)
|
|
else:
|
|
# setup first animation for widget
|
|
self.animations.append(anim)
|
|
self.widgets[widget] = [anim]
|
|
Clock.schedule_once(self.makeAnimStart(
|
|
anim, spos, widget), timedelay)
|
|
|
|
|
|
LAnimationManager = LAnimationMgr()
|
|
|
|
# =============================================================================
|
|
|
|
LSoundLoader = SoundLoader
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LBoxLayout(BoxLayout, LBase):
|
|
def __init__(self, **kw):
|
|
super(LBoxLayout, self).__init__(**kw)
|
|
|
|
def winfo_screenwidth(self):
|
|
return self.size[0]
|
|
|
|
def winfo_screenheight(self):
|
|
return self.size[1]
|
|
|
|
# =============================================================================
|
|
|
|
|
|
def addAnchorOffset(pos, size, anchor):
|
|
# print ('MfxCanvas: anchor=%s' % (anchor))
|
|
x = pos[0]
|
|
y = pos[1]
|
|
xa = 0
|
|
ya = 0
|
|
if anchor == "n":
|
|
ya = -1
|
|
elif anchor == "w":
|
|
xa = -1
|
|
elif anchor == "s":
|
|
ya = 1
|
|
elif anchor == "e":
|
|
xa = 1
|
|
elif anchor == "ne":
|
|
ya = -1
|
|
xa = 1
|
|
elif anchor == "nw":
|
|
ya = -1
|
|
xa = -1
|
|
elif anchor == "se":
|
|
ya = 1
|
|
xa = 1
|
|
elif anchor == "sw":
|
|
ya = 1
|
|
xa = -1
|
|
|
|
if xa == 0:
|
|
x = x - size[0] / 2.0
|
|
elif xa == 1:
|
|
x = x - size[0]
|
|
if ya == 0:
|
|
y = y - size[1] / 2.0
|
|
elif ya == 1:
|
|
y = y - size[1]
|
|
return (x, y)
|
|
|
|
# =============================================================================
|
|
|
|
|
|
def LColorToKivy(outline):
|
|
if (outline[0] == '#'):
|
|
outline = outline[1:]
|
|
ou0 = float(int(outline[0:2], 16)) / 256.0
|
|
ou1 = float(int(outline[2:4], 16)) / 256.0
|
|
ou2 = float(int(outline[4:6], 16)) / 256.0
|
|
ou3 = 1.0
|
|
if len(outline) >= 8:
|
|
ou3 = float(int(outline[6:8], 16)) / 256.0
|
|
return ou0, ou1, ou2, ou3
|
|
|
|
# =============================================================================
|
|
|
|
|
|
def cardfactor(canvas):
|
|
# heuristic to find some sort of 'fontsize' out of the cardset.
|
|
# We take the original small cardsets as reference and calculate
|
|
# a correction factor.
|
|
|
|
def pyth(a, b):
|
|
return math.sqrt(a*a+b*b)
|
|
|
|
cardscale = 1.0
|
|
try:
|
|
cs = canvas.wmain.app.images.cs
|
|
# print('Cardset:', cs)
|
|
# print('Cardset:', cs.type)
|
|
|
|
cardbase = pyth(73, 97)
|
|
if cs.type == CSI.TYPE_FRENCH:
|
|
cardbase = pyth(73, 97)
|
|
elif cs.type == CSI.TYPE_HANAFUDA:
|
|
cardbase = pyth(64, 102)
|
|
elif cs.type == CSI.TYPE_MAHJONGG:
|
|
cardbase = pyth(38, 54)
|
|
elif cs.type == CSI.TYPE_TAROCK:
|
|
cardbase = pyth(80, 80)
|
|
elif cs.type == CSI.TYPE_HEXADECK:
|
|
cardbase = pyth(50, 80)
|
|
elif cs.type == CSI.TYPE_MUGHAL_GANJIFA:
|
|
cardbase = pyth(80, 80)
|
|
elif cs.type == CSI.TYPE_NAVAGRAHA_GANJIFA:
|
|
cardbase = pyth(80, 80)
|
|
elif cs.type == CSI.TYPE_DASHAVATARA_GANJIFA:
|
|
cardbase = pyth(80, 80)
|
|
elif cs.type == CSI.TYPE_TRUMP_ONLY:
|
|
cardbase = pyth(35, 35)
|
|
|
|
si = canvas.wmain.app.images.getSize()
|
|
cardsize = pyth(si[0], si[1])
|
|
cardscale = cardsize/cardbase
|
|
except: # noqa: E722
|
|
pass
|
|
return cardscale
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LText(Widget, LBase):
|
|
text = StringProperty('')
|
|
|
|
def __init__(self, canvas, x, y, **kwargs):
|
|
super(LText, self).__init__(**kwargs)
|
|
# super(LText, self).__init__()
|
|
|
|
if 'text' not in kwargs:
|
|
kwargs['text'] = 'X'
|
|
|
|
font = 'helvetica'
|
|
fontsize = 18.0
|
|
if 'font' in kwargs:
|
|
font = kwargs['font'][0]
|
|
fontsize = kwargs['font'][1]
|
|
del kwargs['font']
|
|
|
|
self.anchor = 'nw'
|
|
if 'anchor' in kwargs:
|
|
self.anchor = kwargs['anchor']
|
|
|
|
self.text = kwargs['text']
|
|
self.coreFontSize = fontsize
|
|
self.coreFont = font
|
|
|
|
# print('LText: font = %s, font_size = %s' % (font, fontsize))
|
|
# print('LText: text = %s' % (self.text))
|
|
|
|
kwargs['font'] = font
|
|
kwargs['font_size'] = fontsize * cardfactor(canvas)
|
|
|
|
class MyLabel(Label, LBase):
|
|
pass
|
|
|
|
self.label = MyLabel(**kwargs)
|
|
self.label.texture_update()
|
|
self.coreSize = self.label.texture_size
|
|
self.corePos = (x, y)
|
|
self.prnt = canvas
|
|
|
|
# print('LText: corePos = %s, coreSize = %s'
|
|
# % (self.corePos, self.coreSize))
|
|
|
|
self.size = self.label.texture_size
|
|
|
|
self.bind(size=self.updateCanvas)
|
|
self.bind(pos=self.updateCanvas)
|
|
self.bind(text=self.updateCanvas)
|
|
|
|
def updateCanvas(self, inst, val):
|
|
self.label.text = self.text
|
|
self.label.texture_update()
|
|
|
|
self.coreSize = self.label.texture_size
|
|
cp = addAnchorOffset(self.corePos, self.coreSize, self.anchor)
|
|
cs = self.coreSize
|
|
|
|
pos, size = self.prnt.CoreToKivy(cp, cs)
|
|
# print('LText: pos = %s, size = %s' % (pos, size))
|
|
|
|
color = LColorToKivy(self.prnt._text_color)
|
|
# print('LText: color = %s' % str(color))
|
|
self.canvas.clear()
|
|
with self.canvas:
|
|
Color(color[0], color[1], color[2], color[3])
|
|
Rectangle(texture=self.label.texture, pos=pos, size=size)
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LEvent(object):
|
|
def __init__(self):
|
|
self.x = 0
|
|
self.y = 0
|
|
self.cardid = -1
|
|
self.char = False
|
|
pass
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LLine(Widget, LBase):
|
|
def __init__(self, canvas, args, **kw):
|
|
super(LLine, self).__init__(**kw)
|
|
|
|
print('kw = %s%s' % (args, kw))
|
|
|
|
lwidth = 10
|
|
fill = '#ee3344'
|
|
ashape = ()
|
|
arrow = 'none'
|
|
|
|
self.prnt = canvas
|
|
xmin = 100000
|
|
ymin = 100000
|
|
xmax = -100000
|
|
ymax = -100000
|
|
self.corePoly = []
|
|
if isinstance(args[0], list):
|
|
kww = args[1]
|
|
if ('width' in kww):
|
|
lwidth = kww['width']
|
|
self.lwidth = lwidth
|
|
if ('fill' in kww):
|
|
fill = kww['fill']
|
|
self.fill = fill
|
|
if ('arrowshape' in kw):
|
|
ashape = kw['arrowshape']
|
|
self.ashape = ashape
|
|
if ('arrow' in kw):
|
|
arrow = kw['arrow']
|
|
self.arrow = arrow
|
|
|
|
pts = args[0]
|
|
ipts = iter(pts)
|
|
for x, y in zip(ipts, ipts):
|
|
print('%s.%s' % (x, y))
|
|
self.corePoly.append(x)
|
|
self.corePoly.append(y)
|
|
if x < xmin:
|
|
xmin = x
|
|
if x > xmax:
|
|
xmax = x
|
|
if y < ymin:
|
|
ymin = y
|
|
if y > ymax:
|
|
ymax = y
|
|
else:
|
|
if ('width' in kw):
|
|
lwidth = kw['width']
|
|
self.lwidth = lwidth
|
|
if ('fill' in kw):
|
|
fill = kw['fill']
|
|
self.fill = fill
|
|
if ('arrowshape' in kw):
|
|
ashape = kw['arrowshape']
|
|
self.ashape = ashape
|
|
if ('arrow' in kw):
|
|
arrow = kw['arrow']
|
|
self.arrow = arrow
|
|
|
|
for i in range(0, 2):
|
|
x = args[2 * i]
|
|
y = args[2 * i + 1]
|
|
self.corePoly.append(x)
|
|
self.corePoly.append(y)
|
|
if x < xmin:
|
|
xmin = x
|
|
if x > xmax:
|
|
xmax = x
|
|
if y < ymin:
|
|
ymin = y
|
|
if y > ymax:
|
|
ymax = y
|
|
|
|
print('width = %s' % self.lwidth)
|
|
print('color = %s' % self.fill)
|
|
print('arrow = %s' % self.arrow)
|
|
print('ashape = %s' % str(self.ashape))
|
|
|
|
self.alist = []
|
|
if self.arrow == 'last':
|
|
self.alist.append(self.corePoly[-2])
|
|
self.alist.append(self.corePoly[-1])
|
|
self.alist.append(self.corePoly[-4])
|
|
self.alist.append(self.corePoly[-3])
|
|
elif self.arrow != 'none':
|
|
self.alist.append(self.corePoly[0])
|
|
self.alist.append(self.corePoly[1])
|
|
self.alist.append(self.corePoly[2])
|
|
self.alist.append(self.corePoly[3])
|
|
|
|
self.corePos = (xmin, ymin)
|
|
self.coreSize = (xmax - xmin, ymax - ymin)
|
|
self.pos = self.corePos
|
|
self.size = self.coreSize
|
|
|
|
self.bcolor = LColorToKivy(self.fill)
|
|
|
|
self.bind(size=self.updateCanvas)
|
|
self.bind(pos=self.updateCanvas)
|
|
|
|
def updateCanvas(self, instance, value):
|
|
# size = self.size
|
|
# pos = self.pos
|
|
|
|
# Linie:
|
|
poly = None
|
|
poly = []
|
|
dmy, sxy = self.prnt.CoreToKivy(
|
|
(0.0, 0.0), (self.lwidth, self.lwidth))
|
|
wpoly = sxy[1]
|
|
ipts = iter(self.corePoly)
|
|
for x, y in zip(ipts, ipts):
|
|
print('%s.%s' % (x, y))
|
|
xy, dmy = self.prnt.CoreToKivy((x, y))
|
|
poly.append(xy[0])
|
|
poly.append(xy[1])
|
|
|
|
def rot(x, y, a):
|
|
x1 = x * math.cos(a) + y * math.sin(a)
|
|
y1 = y * math.cos(a) - x * math.sin(a)
|
|
return (x1, y1)
|
|
|
|
# Pfeil:
|
|
PI = 3.1415926
|
|
atrio = None
|
|
atrio = []
|
|
if (len(self.ashape) > 2):
|
|
dx = (self.alist[0] - self.alist[2])
|
|
dy = (self.alist[1] - self.alist[3])
|
|
if (dx == 0.0):
|
|
if (dy > 0.0):
|
|
ang = -PI / 2.0
|
|
else:
|
|
ang = PI / 2.0
|
|
else:
|
|
ang = math.atan(dy / dx)
|
|
if (dx > 0.0):
|
|
ang = ang + PI
|
|
|
|
# (kante, winkel?)
|
|
x = self.ashape[0] * math.cos(self.ashape[1] * PI / 360.0)
|
|
y = 2.0 * self.ashape[0] * math.sin(self.ashape[1] * PI / 360.0)
|
|
# (länge, breite?)
|
|
# x = self.ashape[0]
|
|
# y = self.ashape[1]
|
|
o = self.ashape[2]
|
|
axy, dmy = self.prnt.CoreToKivy((self.alist[0], self.alist[1]))
|
|
dmy, asxy = self.prnt.CoreToKivy((0, 0), (x, y))
|
|
dmy, aoff = self.prnt.CoreToKivy((0, 0), (o, o))
|
|
print('asxy=%s' % str(asxy))
|
|
|
|
x1, y1 = rot(-aoff[0], 0.0, ang)
|
|
atrio.append(x1 + axy[0])
|
|
atrio.append(y1 + axy[1])
|
|
x1, y1 = rot(asxy[0] - aoff[0], asxy[1], ang)
|
|
atrio.append(x1 + axy[0])
|
|
atrio.append(y1 + axy[1])
|
|
x1, y1 = rot(asxy[0] - aoff[0], -asxy[1], ang)
|
|
atrio.append(x1 + axy[0])
|
|
atrio.append(y1 + axy[1])
|
|
|
|
self.canvas.clear()
|
|
with self.canvas:
|
|
Color(self.bcolor[0], self.bcolor[1],
|
|
self.bcolor[2], self.bcolor[3])
|
|
Line(points=poly, width=wpoly, cap='none', joint='bevel')
|
|
if (len(atrio) > 2):
|
|
Triangle(points=atrio)
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LRectangle(Widget, LBase):
|
|
def __init__(self, prnt, args, **kw):
|
|
super(LRectangle, self).__init__(**kw)
|
|
self.prnt = prnt
|
|
|
|
# print('width %s' % kw['width'])
|
|
# print('outline %s' % kw['outline'])
|
|
# print('fill %s' % kw['fill'])
|
|
|
|
width = 10.0
|
|
if ('width' in kw):
|
|
width = float(kw['width'])
|
|
|
|
bcolor = '#ffa000a0'
|
|
if ('outline') in kw:
|
|
bcolor = kw['outline']
|
|
if (not bcolor or len(bcolor) < 7):
|
|
bcolor = '#ffa000a0'
|
|
|
|
fcolor = '#00aaff20'
|
|
if ('fill') in kw:
|
|
fcolor = kw['fill']
|
|
if (not fcolor or len(fcolor) < 7):
|
|
fcolor = '#00aaff20'
|
|
|
|
self.group = None
|
|
if 'group' in kw:
|
|
self.group = kw['group']
|
|
|
|
xmin = float(args[0])
|
|
ymin = float(args[1])
|
|
xmax = float(args[2])
|
|
ymax = float(args[3])
|
|
|
|
# print ('LRectangle: min = %s.%s' % (xmin, ymin))
|
|
# print ('LRectangle: max = %s.%s' % (xmax, ymax))
|
|
# print ('LRectangle: border = %s' % (width))
|
|
|
|
self.border = width
|
|
self.fcolor = LColorToKivy(fcolor)
|
|
self.bcolor = LColorToKivy(bcolor)
|
|
|
|
self.corePos = (xmin, ymin)
|
|
self.coreSize = (xmax - xmin, ymax - ymin)
|
|
self.pos = self.corePos
|
|
self.size = self.coreSize
|
|
self.topleft = (xmin + width / 2.0, ymin + width / 2.0)
|
|
self.bottomright = (xmax - width / 2.0, ymax - width / 2.0)
|
|
self.poly = None
|
|
|
|
self.bind(size=self.updateCanvas)
|
|
self.bind(pos=self.updateCanvas)
|
|
|
|
def updateCanvas(self, instance, value):
|
|
# print('LRectangle: updateCanvas')
|
|
|
|
pos, size = self.prnt.CoreToKivy(self.corePos, self.coreSize)
|
|
bpos, dmy = self.prnt.CoreToKivy(self.topleft)
|
|
tpos, dmy = self.prnt.CoreToKivy(self.bottomright)
|
|
|
|
poly = [bpos[0], bpos[1],
|
|
tpos[0], bpos[1],
|
|
tpos[0], tpos[1],
|
|
bpos[0], tpos[1],
|
|
bpos[0], bpos[1]]
|
|
|
|
cf = cardfactor(self.prnt)
|
|
dmy, brd = self.prnt.CoreToKivy(
|
|
(0.0, 0.0), (self.border, self.border))
|
|
border = brd[1] * cf
|
|
|
|
self.canvas.clear()
|
|
with self.canvas:
|
|
Color(self.fcolor[0], self.fcolor[1],
|
|
self.fcolor[2], self.fcolor[3])
|
|
Rectangle(pos=pos, size=size)
|
|
Color(self.bcolor[0], self.bcolor[1],
|
|
self.bcolor[2], self.bcolor[3])
|
|
Line(points=poly, width=border)
|
|
|
|
def on_touch_down(self, touch):
|
|
if self.collide_point(*touch.pos):
|
|
if self.group is not None:
|
|
logging.info('LRectangle: self=%s group=%s' %
|
|
(self, self.group))
|
|
if '<1>' in self.group.bindings:
|
|
# logging.info('LRectangle: size=%s' % (self.size))
|
|
ppos, psize = self.group.canvas.KivyToCore(touch.pos)
|
|
event = LEvent()
|
|
event.x = ppos[0]
|
|
event.y = ppos[1]
|
|
if touch.is_double_tap:
|
|
self.group.bindings['<Double-1>'](event)
|
|
else:
|
|
self.group.bindings['<1>'](event)
|
|
return True
|
|
return False
|
|
|
|
def on_touch_up(self, touch):
|
|
if self.collide_point(*touch.pos):
|
|
if self.group is not None:
|
|
logging.info('LRectangle: self=%s group=%s' %
|
|
(self, self.group))
|
|
if '<ButtonRelease-1>' in self.group.bindings:
|
|
ppos, psize = self.group.canvas.KivyToCore(touch.pos)
|
|
event = LEvent()
|
|
event.x = ppos[0]
|
|
event.y = ppos[1]
|
|
self.group.bindings['<ButtonRelease-1>'](event)
|
|
return True
|
|
return False
|
|
|
|
# =============================================================================
|
|
# Represents a Card as Kivy Window. Will contain an LImage item as child.
|
|
# Images are managed in cards.py according to the cards state. Processes
|
|
# Events/Action on the card.
|
|
|
|
|
|
class LImageItem(BoxLayout, LBase):
|
|
def __init__(self, **kw):
|
|
super(LImageItem, self).__init__(**kw)
|
|
self.game = None
|
|
self.card = None
|
|
self.group = None
|
|
if 'game' in kw:
|
|
self.game = kw['game']
|
|
if 'card' in kw:
|
|
self.card = kw['card']
|
|
if 'group' in kw:
|
|
self.group = kw['group']
|
|
self.dragstart = None
|
|
# ev. noch globales cache für stacks->game und cards->stack
|
|
# einrichten. Aber: stacks hängt vom jeweiligen spiel ab.
|
|
|
|
def __str__(self):
|
|
return f'<LImageItem @ {hex(id(self))}>'
|
|
|
|
def send_event_pressed_n(self, event, n):
|
|
if self.group and n in self.group.bindings:
|
|
self.group.bindings[n](event)
|
|
|
|
def send_event_pressed(self, touch, event):
|
|
|
|
if touch.is_double_tap:
|
|
self.send_event_pressed_n(event, '<Double-1>')
|
|
else:
|
|
button = 'left'
|
|
if 'button' in touch.profile:
|
|
button = touch.button
|
|
if button == 'left':
|
|
self.send_event_pressed_n(event, '<1>')
|
|
return
|
|
if button == 'middle':
|
|
self.send_event_pressed_n(event, '<2>')
|
|
return
|
|
if button == 'right':
|
|
self.send_event_pressed_n(event, '<3>')
|
|
return
|
|
|
|
def on_touch_down(self, touch):
|
|
|
|
print('LCardImage: size = %s' % self.size)
|
|
if self.collide_point(*touch.pos):
|
|
|
|
for c in self.children:
|
|
# print('child at %s' % c)
|
|
if (c.on_touch_down(touch) and self.game):
|
|
for stack in self.game.allstacks:
|
|
for i in range(len(stack.cards)):
|
|
if stack.cards[i] == self.card:
|
|
print('LCardImage: stack = %s' % stack)
|
|
print('LCardImage: touch = %s' % str(touch))
|
|
print('grab')
|
|
# grab the touch!
|
|
touch.grab(self)
|
|
ppos, psize = self.game.canvas.KivyToCore(
|
|
touch.pos, self.size)
|
|
event = LEvent()
|
|
event.x = ppos[0]
|
|
event.y = ppos[1]
|
|
self.dragstart = touch.pos
|
|
event.cardid = i
|
|
self.send_event_pressed(touch, event)
|
|
return True
|
|
|
|
if self.group is not None:
|
|
print('LCardImage: self=%s group=%s' % (self, self.group))
|
|
if '<1>' in self.group.bindings:
|
|
ppos, psize = self.group.canvas.KivyToCore(
|
|
touch.pos, self.size)
|
|
event = LEvent()
|
|
event.x = ppos[0]
|
|
event.y = ppos[1]
|
|
self.group.bindings['<1>'](event)
|
|
return True
|
|
|
|
if self.card is None:
|
|
return False
|
|
if self.game is None:
|
|
return False
|
|
|
|
# print('LCardImage: touch_down on %s' % str(touch.pos))
|
|
return False
|
|
|
|
def send_event_released_1(self, event):
|
|
if self.group and '<ButtonRelease-1>' in self.group.bindings:
|
|
self.group.bindings['<ButtonRelease-1>'](event)
|
|
|
|
def on_touch_up(self, touch):
|
|
if touch.grab_current is self:
|
|
# release my grabbed touch!
|
|
print('ungrab')
|
|
touch.ungrab(self)
|
|
return True
|
|
|
|
if self.collide_point(*touch.pos):
|
|
|
|
for c in self.children:
|
|
# print('child at %s' % c)
|
|
|
|
if (c.on_touch_up(touch) and self.game):
|
|
for stack in self.game.allstacks:
|
|
for i in range(len(stack.cards)):
|
|
if stack.cards[i] == self.card:
|
|
print('LCardImage: stack = %s' % stack)
|
|
ppos, psize = self.game.canvas.KivyToCore(
|
|
touch.pos, self.size)
|
|
event = LEvent()
|
|
event.x = ppos[0]
|
|
event.y = ppos[1]
|
|
event.cardid = i
|
|
self.send_event_released_1(event)
|
|
return True
|
|
|
|
if self.group is not None:
|
|
print('LCardImage: self=%s group=%s' % (self, self.group))
|
|
if '<ButtonRelease-1>' in self.group.bindings:
|
|
ppos, psize = self.group.canvas.KivyToCore(
|
|
touch.pos, self.size)
|
|
event = LEvent()
|
|
event.x = ppos[0]
|
|
event.y = ppos[1]
|
|
self.group.bindings['<ButtonRelease-1>'](event)
|
|
return True
|
|
|
|
if self.card is None:
|
|
return False
|
|
if self.game is None:
|
|
return False
|
|
|
|
# print('LCardImage: touch_up on %s' % str(touch.pos))
|
|
return False
|
|
|
|
def on_touch_move(self, touch):
|
|
# behandeln nur wenn grabbed
|
|
if touch.grab_current is not self:
|
|
return False
|
|
if 'pos' not in touch.profile:
|
|
return False
|
|
|
|
print('LCardImage: touch_move on %s' % str(touch.pos))
|
|
|
|
for stack in self.game.allstacks:
|
|
for i in range(len(stack.cards)):
|
|
if stack.cards[i] == self.card:
|
|
print('LCardImage: stack = %s/%s' % (stack, touch))
|
|
ppos, psize = self.game.canvas.KivyToCore(
|
|
touch.pos, self.size)
|
|
event = LEvent()
|
|
event.x = ppos[0]
|
|
event.y = ppos[1]
|
|
event.cardid = i
|
|
stack._motionEventHandler(event)
|
|
return True
|
|
|
|
# print('LCardImage: touch_move on %s' % str(touch.pos))
|
|
return False
|
|
|
|
# =============================================================================
|
|
# Treeview
|
|
|
|
|
|
class LTreeRoot(TreeView, LBase):
|
|
def __init__(self, **kw):
|
|
super(LTreeRoot, self).__init__(**kw)
|
|
self.kw = kw
|
|
|
|
def closeLastNode(self):
|
|
ret = False
|
|
lastopen = None
|
|
for ti in reversed(self.children):
|
|
if isinstance(ti, LTreeNode):
|
|
if ti.is_open:
|
|
lastopen = ti
|
|
|
|
if lastopen is not None:
|
|
self.toggle_node(lastopen)
|
|
self.select_node(lastopen)
|
|
ret = True
|
|
|
|
return ret
|
|
|
|
|
|
class LTreeNode(ButtonBehavior, TreeViewLabel, LBase):
|
|
|
|
# def __init__(self, gameview, **kw):
|
|
def __init__(self, **kw):
|
|
self.command = None
|
|
if 'command' in kw:
|
|
self.command = kw['command']
|
|
self.variable = None
|
|
if 'variable' in kw:
|
|
self.variable = kw['variable']
|
|
if 'value' in kw:
|
|
self.value = kw['value']
|
|
if ('text' in kw):
|
|
self.title = kw['text']
|
|
|
|
super(LTreeNode, self).__init__(markup=True, **kw)
|
|
|
|
if self.variable:
|
|
self.variable.bind(value=self.onVarChange)
|
|
self.onVarChange(self.variable, self.variable.get())
|
|
|
|
# self.gameview = gameview
|
|
self.coreFont = self.font_size
|
|
# self.scaleFont(self.gameview.size[1])
|
|
# self.gameview.bind(size=self.scaleFontCB)
|
|
# nicht skalieren!
|
|
|
|
self.bind(on_release=self.on_released)
|
|
self.bind(is_selected=self.onSelect)
|
|
self.bind(is_open=self.onOpen)
|
|
|
|
def onVarChange(self, instance, value):
|
|
# print('LTreeNode: onVarChange(%s, %s, %s)'
|
|
# % (instance, value, type(value)))
|
|
if type(value) is bool:
|
|
self.setCheck(value)
|
|
if type(value) is int:
|
|
self.setVal(value)
|
|
if type(value) is str:
|
|
self.setVal(value)
|
|
# if type(value) is unicode:
|
|
# self.setVal(value)
|
|
|
|
def setCheck(self, value):
|
|
# print('LTreeNode: setCheck(%s)' % value)
|
|
if value:
|
|
# self.text = '+ '+self.title
|
|
self.text = '[b]+[/b] ' + self.title
|
|
else:
|
|
self.text = '- ' + self.title
|
|
self.texture_update()
|
|
|
|
def setVal(self, value):
|
|
# print('LTreeNode: setVal(%s)' % value)
|
|
if value == self.value:
|
|
# fs = str(int(self.font_size+2))
|
|
# print ('%s.%s' % (self.font_size, fs))
|
|
# self.text = '[size='+fs+'][b]'+self.title+'[/b][/size]'
|
|
# self.text = 'o '+self.title
|
|
self.text = '[b]o[/b] ' + self.title
|
|
# self.text = u'\u25cf '+self.title # unicode filled circle
|
|
else:
|
|
self.text = self.title
|
|
self.text = u' ' + self.title
|
|
# self.text = u'\u25cb '+self.title # unicode open circle
|
|
self.texture_update()
|
|
|
|
# font skalierung.
|
|
|
|
def scaleFont(self, value):
|
|
self.font_size = int(self.coreFont * value / 550.0)
|
|
|
|
def scaleFontCB(self, instance, value):
|
|
self.scaleFont(value[1])
|
|
|
|
# benutzer interaktion.
|
|
|
|
def onSelect(self, instance, val):
|
|
if val:
|
|
print('select %s' % self.title)
|
|
else:
|
|
print('deselect %s' % self.title)
|
|
pass
|
|
|
|
def collapseChildren(self, deep=False):
|
|
|
|
def cc(p, n):
|
|
for c in n.nodes:
|
|
if c.is_open:
|
|
cc(p, c)
|
|
p.toggle_node(c)
|
|
|
|
p = self.parent
|
|
if p and isinstance(p, LTreeRoot):
|
|
for n in self.nodes:
|
|
if n.is_open:
|
|
# n.collapseChildren() # ginge nur mit LTreeNode!
|
|
if deep:
|
|
cc(p, n) # geht mit allen TreeViewNode
|
|
p.toggle_node(n)
|
|
|
|
def collapseSiblings(self, deep=True):
|
|
|
|
def cc(p, n):
|
|
for c in n.nodes:
|
|
if c.is_open:
|
|
cc(p, c)
|
|
p.toggle_node(c)
|
|
|
|
p = self.parent
|
|
if p and isinstance(p, LTreeRoot):
|
|
# print('expand: LTreeRoot')
|
|
for n in p.root.nodes:
|
|
# print('expand: -> check %s' % n.title)
|
|
if n != self and n.is_open and n.level >= self.level:
|
|
# print('expand: -> close %s' % n.title)
|
|
if deep:
|
|
cc(p, n)
|
|
p.toggle_node(n)
|
|
|
|
pn = self.parent_node
|
|
if pn and isinstance(pn, LTreeNode):
|
|
# print('expand: LTreeNode')
|
|
for n in pn.nodes:
|
|
# print('expand: -> check %s' % n.title)
|
|
if n != self and n.is_open and n.level >= self.level:
|
|
# print('expand: -> close %s' % n.title)
|
|
if deep:
|
|
cc(p, n)
|
|
p.toggle_node(n)
|
|
|
|
def onOpen(self, instance, val):
|
|
if val:
|
|
# print('expand %s, %s' % (self.level, self.title))
|
|
self.collapseSiblings(deep=False)
|
|
else:
|
|
# print('collapse %s, %s' % (self.level, self.title))
|
|
pass
|
|
|
|
def on_released(self, v):
|
|
if self.command:
|
|
Clock.schedule_once(self.commandCB, 0.1)
|
|
else:
|
|
Clock.schedule_once(self.toggleCB, 0.1)
|
|
|
|
def commandCB(self, d):
|
|
self.command()
|
|
|
|
def toggleCB(self, d):
|
|
# hier könnte der knoten ev. auch neu aufgebaut werden ?!
|
|
self.parent.toggle_node(self)
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LTopLevelContent(BoxLayout, LBase):
|
|
def __init__(self, **kw):
|
|
super(LTopLevelContent, self).__init__(**kw)
|
|
|
|
# beispiel zu canvas (hintergrund)
|
|
with self.canvas.before:
|
|
Color(0.45, 0.5, 0.5, 1.0)
|
|
self.rect = Rectangle(pos=self.pos, size=self.size)
|
|
self.bind(pos=self.update_rect)
|
|
self.bind(size=self.update_rect)
|
|
|
|
def update_rect(self, *args):
|
|
self.rect.pos = self.pos
|
|
self.rect.size = self.size
|
|
|
|
def wm_minsize(self, w, h):
|
|
pass
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LTopLine(ButtonBehavior, Label, LBase):
|
|
|
|
def __init__(self, **kw):
|
|
super(LTopLine, self).__init__(**kw)
|
|
with self.canvas.before:
|
|
Color(0.45, 0.3, 0.3, 1.0)
|
|
self.rect = Rectangle(pos=self.pos, size=self.size)
|
|
self.bind(pos=self.update_rect)
|
|
self.bind(size=self.update_rect)
|
|
|
|
def update_rect(self, *args):
|
|
self.rect.pos = self.pos
|
|
self.rect.size = self.size
|
|
|
|
def on_press(self):
|
|
print('press')
|
|
|
|
def on_release(self):
|
|
print('release')
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LTopLevel0(BoxLayout, LBase):
|
|
def __init__(self, top, title=None, **kw):
|
|
self.main = top
|
|
super(LTopLevel0, self).__init__(
|
|
orientation="vertical", **kw)
|
|
|
|
# self.canvas.add(Color(0, 1, 0, 0.4))
|
|
# self.canvas.add(Rectangle(pos=(100, 100), size=(100, 100)))
|
|
|
|
self.size_hint = (0.5, 1.0)
|
|
'''
|
|
self.titleline = BoxLayout(
|
|
orientation="horizontal", size_hint=[1.0, 0.15], **kw)
|
|
self.button = Button(text="X", size_hint=[0.15, 1.0], **kw)
|
|
if not title:
|
|
title = '<>'
|
|
self.title = Label(text=title, **kw)
|
|
self.titleline.add_widget(self.title)
|
|
self.titleline.add_widget(self.button)
|
|
'''
|
|
self.titleline = LTopLine(text=title, size_hint=[1.0, 0.15])
|
|
self.title = title
|
|
|
|
# self.content = BoxLayout(orientation="vertical", **kw)
|
|
self.content = LTopLevelContent(orientation="vertical", **kw)
|
|
self.add_widget(self.titleline)
|
|
self.add_widget(self.content)
|
|
'''
|
|
self.button.bind(on_press=self.onClick)
|
|
'''
|
|
self.titleline.bind(on_press=self.onClick)
|
|
self.main.pushWork(self.title, self)
|
|
|
|
def onClick(self, event):
|
|
print('LTopLevel: onClick')
|
|
self.main.popWork(self.title)
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LTopLevel(BoxLayout, LBase):
|
|
def __init__(self, parent, title=None, **kw):
|
|
self.mainwindow = parent
|
|
super(LTopLevel, self).__init__(
|
|
orientation="vertical", **kw)
|
|
|
|
if ('size_hint' not in kw):
|
|
self.size_hint = (0.5, 1.0)
|
|
else:
|
|
del kw['size_hint']
|
|
self.titleline = LTopLine(text=title, size_hint=(1.0, 0.10))
|
|
|
|
self.content = LTopLevelContent(orientation="vertical", **kw)
|
|
self.add_widget(self.titleline)
|
|
self.add_widget(self.content)
|
|
|
|
def processAndroidBack(self):
|
|
ret = False
|
|
# try to collapse the last open tree node
|
|
# the treeview will be located inside of a scrollview
|
|
# (-> menubar.py)
|
|
for c in self.content.children:
|
|
# print("childitem: %s" % str(c))
|
|
if isinstance(c, LScrollView):
|
|
for t in reversed(c.children):
|
|
# print(" childitem: %s" % str(t))
|
|
if isinstance(t, LTreeRoot):
|
|
ret = t.closeLastNode()
|
|
if isinstance(c, BoxLayout):
|
|
for t in reversed(c.children):
|
|
# print(" childitem: %s" % str(t))
|
|
if isinstance(t, LPopCommander):
|
|
ret = t.pop()
|
|
pass
|
|
return ret
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LMenuBar(BoxLayout, LBase):
|
|
def __init__(self, **kw):
|
|
super(LMenuBar, self).__init__(**kw)
|
|
self.menu = None
|
|
self.size_hint = (1.0, 0.08)
|
|
|
|
def setMenu(self, menu):
|
|
print('LMenuBar: setMenu %s, %s' % (self, menu))
|
|
|
|
# Letztes Menu entfernen
|
|
last = self.menu
|
|
if (last is not None):
|
|
self.remove_widget(last)
|
|
self.menu = None
|
|
|
|
# Neues Menu einfügen
|
|
if (menu is not None):
|
|
self.add_widget(menu)
|
|
self.menu = menu
|
|
menu.setBar(self)
|
|
|
|
def getMenu(self):
|
|
return self.menu
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LMenu(ActionView, LBase):
|
|
def __init__(self, prev, **kw):
|
|
super(LMenu, self).__init__(**kw)
|
|
|
|
class MyActionPrev(ActionPrevious, LBase):
|
|
pass
|
|
|
|
kw['app_icon'] = 'data/images/icons/48x48/pysol.png'
|
|
kw['with_previous'] = prev
|
|
kw['size_hint'] = (.01, 1)
|
|
self.ap = MyActionPrev(**kw)
|
|
self.add_widget(self.ap)
|
|
self.bar = None
|
|
self.uppermenu = None
|
|
|
|
def addItem(self, mi):
|
|
# print ('LMenu: addItem '+str(mi)+' '+str(self.bar))
|
|
mi.setBar(self.bar)
|
|
self.add_widget(mi)
|
|
|
|
def setBar(self, bar):
|
|
# print ('LMenu: setBar %s, %s' % (self, bar))
|
|
self.bar = bar
|
|
|
|
def prev(self, menu):
|
|
# print ('LMenu: prev = %s' % menu)
|
|
self.uppermenu = menu
|
|
self.ap.bind(on_release=self.upper)
|
|
pass
|
|
|
|
def upper(self, event):
|
|
print('upper')
|
|
self.bar.setMenu(self.uppermenu)
|
|
|
|
def delete(self, pos, mode):
|
|
# print ('LMenu(%s): delete(%s, %s)' % (self, pos, mode))
|
|
|
|
items = []
|
|
menues = []
|
|
for c in self.children:
|
|
if (type(c) is LMenuItem):
|
|
# print ('LMenu: to delete child %s' % c)
|
|
items.append(c)
|
|
elif (type(c) is LMenu):
|
|
# print ('LMenu: to delete child %s' % c)
|
|
menues.append(c)
|
|
else:
|
|
# print ('LMenu: unknown child %s' % c)
|
|
pass
|
|
|
|
for c in items:
|
|
# print ('LMenu: delete child %s' % c)
|
|
self.clear_widgets([c])
|
|
for c in menues:
|
|
# print ('LMenu: delete child %s' % c)
|
|
self.clear_widgets([c])
|
|
c.delete(pos, mode)
|
|
|
|
# def __str__(self):
|
|
# return hex(id(self))
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LMenuItem(ActionButton, LBase):
|
|
|
|
def __init__(self, menu, **kw):
|
|
super(LMenuItem, self).__init__(**kw)
|
|
# super(LMenuItem, self).__init__()
|
|
self.bar = None
|
|
self.submenu = None
|
|
self.menu = menu
|
|
self.menu.addItem(self)
|
|
self.minimum_width = '200sp'
|
|
if 'command' in kw:
|
|
self.setCommand(kw['command'])
|
|
if 'submenu' in kw:
|
|
self.setSubMenu(kw['submenu'])
|
|
|
|
def setBar(self, bar):
|
|
# print ('LMenuItem: setBar %s, %s' % (self, bar))
|
|
self.bar = bar
|
|
|
|
def onClick(self, event):
|
|
# print('LMenuItem: onClick')
|
|
# print('LMenuItem: submenu vorh: '+str(self.submenu))
|
|
self.bar.setMenu(self.submenu)
|
|
return True
|
|
|
|
def setSubMenu(self, submenu):
|
|
# print('LMenuItem: setSubMenu')
|
|
self.submenu = submenu
|
|
# print('LMenuItem: setSubMenu: '+str(self.submenu))
|
|
self.submenu.prev(self.menu)
|
|
self.submenu.setBar(self.bar)
|
|
self.bind(on_release=self.onClick)
|
|
pass
|
|
|
|
def setCommand(self, cmd):
|
|
# print('LMenuItem: setCommand')
|
|
self.bind(on_release=cmd)
|
|
|
|
# def __str__(self):
|
|
# return hex(id(self))
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LScrollView(ScrollView, LBase):
|
|
def __init__(self, **kw):
|
|
super(LScrollView, self).__init__(**kw)
|
|
self.delayDown = False
|
|
self.touch = None
|
|
|
|
def delayReset(self, dt):
|
|
if not self.delayDown:
|
|
return
|
|
self.delayDown = False
|
|
ScrollView.on_touch_down(self, self.touch)
|
|
|
|
# Scroll ist original viel zu flink auf den Touchgeräten.
|
|
# Wir versuchen das hier etwas abzuschwächen.
|
|
|
|
def on_touch_down(self, touch):
|
|
self.delayDown = True
|
|
self.touch = touch
|
|
Clock.schedule_once(self.delayReset, 0.15)
|
|
|
|
def on_touch_up(self, touch):
|
|
if self.delayDown:
|
|
ScrollView.on_touch_down(self, self.touch)
|
|
self.delayDown = False
|
|
return ScrollView.on_touch_up(self, touch)
|
|
|
|
def on_touch_move(self, touch):
|
|
return ScrollView.on_touch_move(self, touch)
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LWorkWindow(Widget):
|
|
def __init__(self):
|
|
super(LWorkWindow, self).__init__()
|
|
|
|
# beispiel zu canvas (hintergrund)
|
|
with self.canvas.before:
|
|
Color(0, 1, 1, 0.4)
|
|
self.rect = Rectangle(pos=self.pos, size=self.size)
|
|
self.bind(pos=self.update_rect)
|
|
self.bind(size=self.update_rect)
|
|
|
|
def update_rect(self, *args):
|
|
self.rect.pos = self.pos
|
|
self.rect.size = self.size
|
|
|
|
def on_touch_down(self, touch):
|
|
print('LWorkWindow: touch_down on %s' % str(touch.pos))
|
|
# return True
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LTkBase:
|
|
# Tk Emulation needs.
|
|
def __init__(self):
|
|
self.title = "default title"
|
|
self.icontitle = "default title"
|
|
logging.info("LTkBase: __init__()")
|
|
self.sleeping = False
|
|
self.in_loop = False
|
|
self.screenSize = (1000, 1000)
|
|
|
|
def cget(self, strg):
|
|
return False
|
|
|
|
def wm_title(self, strg):
|
|
self.title = strg
|
|
logging.info("LTkBase: wm_title %s" % strg)
|
|
if (self.app):
|
|
# self.app.top.topLine.text = strg
|
|
self.app.top.getMenu().ap.title = strg
|
|
|
|
def wm_iconname(self, strg):
|
|
self.icontitle = strg
|
|
logging.info("LTkBase: wm_iconname %s" % strg)
|
|
|
|
def eval_screen_dim(self, size):
|
|
self.screenSize = size
|
|
if get_platform() == 'android':
|
|
from jnius import autoclass
|
|
from jnius import cast
|
|
else:
|
|
return
|
|
|
|
PythonActivity = autoclass('org.kivy.android.PythonActivity')
|
|
currentActivity = cast(
|
|
'android.app.Activity', PythonActivity.mActivity)
|
|
wm = currentActivity.getWindowManager()
|
|
d = wm.getDefaultDisplay()
|
|
|
|
self.screenSize = (d.getWidth(), d.getHeight())
|
|
|
|
def winfo_screenwidth(self):
|
|
logging.info("LTkBase: winfo_screenwidth %s" % str(self.size[0]))
|
|
return self.size[0]
|
|
|
|
def winfo_screenheight(self):
|
|
logging.info("LTkBase: winfo_screenheight %s" % str(self.size[1]))
|
|
return self.size[1]
|
|
|
|
def winfo_screendepth(self):
|
|
return 32
|
|
|
|
def wm_minsize(self, x, y):
|
|
pass
|
|
|
|
def option_add(self, a, b, c):
|
|
pass
|
|
|
|
def option_get(self, a, b):
|
|
return 0
|
|
|
|
def wm_withdraw(self):
|
|
logging.info("LTkBase: wm_withdraw")
|
|
pass
|
|
|
|
def busyUpdate(self):
|
|
print('LTkBase: busyUpdate()')
|
|
pass
|
|
|
|
def grid_columnconfigure(self, a, weight):
|
|
pass
|
|
|
|
def grid_rowconfigure(self, a, weight):
|
|
pass
|
|
|
|
def connectApp(self, app):
|
|
logging.info("LTkBase: connectApp %s" % str(app))
|
|
self.app = app
|
|
pass
|
|
|
|
def wm_geometry(self, val):
|
|
logging.info("LTkBase: wm_geometry %s" % str(val))
|
|
pass
|
|
|
|
def update_idletasks(self):
|
|
# logging.info("LTkBase: update_idletasks")
|
|
try:
|
|
if len(EventLoop.event_listeners) > 0:
|
|
self.in_loop = True
|
|
EventLoop.idle()
|
|
self.in_loop = False
|
|
else:
|
|
logging.info("LTkBase: update_idletasks: terminating")
|
|
except Exception:
|
|
self.in_loop = False
|
|
logging.info("LTkBase: update_idletasks: exception")
|
|
|
|
def wm_state(self):
|
|
return ""
|
|
|
|
def wm_deiconify(self):
|
|
pass
|
|
|
|
def mainloop(self):
|
|
logging.info("LTkBase: mainloop")
|
|
pass
|
|
|
|
def quit(self):
|
|
logging.info("LTkBase: quit")
|
|
stopTouchApp()
|
|
|
|
def interruptSleep(self):
|
|
# logging.info('LTkBase: interruptSleep')
|
|
self.update_idletasks()
|
|
# self.sleep_var = 1
|
|
return
|
|
|
|
def mainquit(self):
|
|
logging.info('LTkBase: mainquit')
|
|
lapp = App.get_running_app()
|
|
lapp.mainloop.send(None) # Spielprozess verlassen
|
|
return
|
|
|
|
def onWakeUp(self, dt):
|
|
self.sleeping = False
|
|
|
|
def sleep(self, seconds):
|
|
logging.info('LTkBase: sleep %s seconds' % seconds)
|
|
self.sleeping = True
|
|
Clock.schedule_once(self.onWakeUp, seconds)
|
|
while self.sleeping:
|
|
# time.sleep(0.05)
|
|
self.in_loop = True
|
|
EventLoop.idle()
|
|
self.in_loop = False
|
|
|
|
def waitCondition(self, condition, swallow=False, pickup=False):
|
|
# logging.info('LTkBase: wait condition start')
|
|
while condition():
|
|
self.in_loop = True
|
|
if swallow: # eat picked input up
|
|
for provider in EventLoop.input_providers:
|
|
provider.update(dispatch_fn=lambda *x: None)
|
|
EventLoop.idle()
|
|
if pickup: # pick input from os
|
|
if EventLoop.window:
|
|
EventLoop.window.mainloop()
|
|
self.in_loop = False
|
|
# logging.info('LTkBase: wait condition end')
|
|
|
|
def waitAnimation(self, swallow=False, pickup=False):
|
|
self.waitCondition(LAnimationManager.checkRunning,
|
|
swallow=swallow,
|
|
pickup=pickup)
|
|
|
|
def tkraise(self):
|
|
pass
|
|
|
|
def winfo_ismapped(self):
|
|
return True
|
|
# ???
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LStack:
|
|
def __init__(self):
|
|
self.items = []
|
|
|
|
def isEmpty(self):
|
|
return self.items == []
|
|
|
|
def push(self, key, item):
|
|
self.items.append((key, item))
|
|
|
|
def pop(self, key):
|
|
for i in range(len(self.items)):
|
|
t = self.items[i]
|
|
if (t[0] == key):
|
|
self.items.pop(i)
|
|
return t[1]
|
|
return None
|
|
|
|
def peek(self, key):
|
|
for i in range(len(self.items)):
|
|
t = self.items[i]
|
|
if (t[0] == key):
|
|
return t
|
|
return None
|
|
|
|
def size(self):
|
|
return len(self.items)
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LMainWindow(BoxLayout, LTkBase):
|
|
|
|
longPress = NumericProperty(0)
|
|
|
|
def __init__(self, **kw):
|
|
super(LMainWindow, self).__init__(orientation='vertical')
|
|
LTkBase.__init__(self)
|
|
self.menuArea = LMenuBar()
|
|
self.workContainer = LBoxLayout(orientation='horizontal')
|
|
self.workContainerO = LBoxLayout(orientation='horizontal')
|
|
self.workContainer1 = LBoxLayout(orientation='vertical')
|
|
self.workArea = None
|
|
self.toolBar = None
|
|
self.toolBarPos = 0
|
|
self.bindings = {}
|
|
self._w = '.'
|
|
|
|
self.add_widget(self.menuArea)
|
|
self.add_widget(self.workContainerO)
|
|
self.workContainerO.add_widget(self.workContainer)
|
|
|
|
self.workStack = LStack()
|
|
self.app = None
|
|
|
|
'''
|
|
from kivy.graphics import opengl_utils
|
|
print('OPENGL support:')
|
|
print(opengl_utils.gl_get_extensions())
|
|
'''
|
|
|
|
# self.touches = []
|
|
|
|
# beispiel zu canvas (hintergrund)
|
|
# with self.canvas.before:
|
|
# Color(0, 1, 0.7, 0.5)
|
|
# self.rect = Rectangle(pos=self.pos, size=self.size)
|
|
# self.bind(pos=self.update_rect)
|
|
# self.bind(size=self.update_rect)
|
|
|
|
# def update_rect(self, *args):
|
|
# self.rect.pos = self.pos
|
|
# self.rect.size = self.size
|
|
|
|
def on_motion(self, m):
|
|
print('on_motion', m)
|
|
pass
|
|
|
|
# Events.
|
|
|
|
def on_touch_down(self, touch):
|
|
ret = False
|
|
|
|
if super().on_touch_down(touch):
|
|
return True
|
|
|
|
# print(dir(touch))
|
|
|
|
# multitouch detection
|
|
'''
|
|
#print("MainWindow touch_down",touch.ox,touch.oy)
|
|
#print("MainWindow touch_down",touch.sx,touch.sy)
|
|
#print("MainWindow touch_down",touch.px,touch.py)
|
|
self.touches.append(touch)
|
|
print("touches cnt = ",len(self.touches))
|
|
'''
|
|
# multiclick detection
|
|
'''
|
|
if touch.is_double_tap:
|
|
# print('Touch is a double tap !')
|
|
# print(' - interval is', touch.double_tap_time)
|
|
# print(' - distance betw. previous is', touch.double_tap_distance)
|
|
# test the functions of Android back key
|
|
ret = self.processAndroidBack()
|
|
if (ret):
|
|
return ret
|
|
'''
|
|
'''
|
|
if touch.is_triple_tap:
|
|
print('Touch is a triple tap !')
|
|
print(' - interval is', touch.triple_tap_time)
|
|
print(' - distance between previous is', touch.triple_tap_distance)
|
|
'''
|
|
# (Eventloop reentrancy check)
|
|
if self.in_loop:
|
|
return ret
|
|
|
|
# (demo mode stop - nur auf spielfläche)
|
|
if '<KeyPress>' in self.bindings:
|
|
pgs = self.workStack.peek('playground')
|
|
if pgs:
|
|
pg = pgs[1]
|
|
if pg.collide_point(*touch.pos):
|
|
event = LEvent()
|
|
event.char = True
|
|
self.bindings['<KeyPress>'](event)
|
|
|
|
# standard notifikation:
|
|
for c in self.children:
|
|
ret = c.on_touch_down(touch)
|
|
if ret:
|
|
break
|
|
return ret
|
|
|
|
def on_touch_up(self, touch):
|
|
ret = False
|
|
if super().on_touch_up(touch):
|
|
return True
|
|
|
|
# long press only on playground.
|
|
pgs = self.workStack.peek('playground')
|
|
if pgs:
|
|
pg = pgs[1]
|
|
if pg.collide_point(*touch.pos):
|
|
if (touch.time_end-touch.time_start) > 2.5:
|
|
self.longPress = touch.time_end
|
|
|
|
# standard notifikation:
|
|
for c in self.children:
|
|
ret = c.on_touch_up(touch)
|
|
if ret:
|
|
break
|
|
|
|
# multitouch support
|
|
'''
|
|
self.touches = [xx for xx in self.touches if xx != touch]
|
|
print("touches cnt = ",len(self.touches))
|
|
'''
|
|
return ret
|
|
|
|
def on_longPress(self, instance, timestamp):
|
|
print('longPressed at {time}'.format(time=timestamp))
|
|
AndroidScreenRotation.lock()
|
|
|
|
# Menubar:
|
|
|
|
def setMenu(self, menu):
|
|
self.menuArea.setMenu(menu)
|
|
|
|
def getMenu(self):
|
|
return self.menuArea.getMenu()
|
|
|
|
# Toolbar:
|
|
|
|
def setTool(self, toolbar, pos=0):
|
|
if (toolbar is not None):
|
|
self.toolBar = toolbar
|
|
self.toolBarPos = pos
|
|
self.rebuildContainer()
|
|
|
|
# Workarea:
|
|
|
|
def removeContainer(self):
|
|
self.workContainer.clear_widgets()
|
|
self.workContainerO.clear_widgets()
|
|
self.workContainer1.clear_widgets()
|
|
|
|
def buildContainer(self):
|
|
# (hbox)
|
|
if self.toolBar is not None and self.toolBarPos == 3:
|
|
self.workContainerO.add_widget(self.toolBar)
|
|
self.workContainerO.add_widget(self.workContainer1)
|
|
if self.toolBar is not None and self.toolBarPos == 4:
|
|
self.workContainerO.add_widget(self.toolBar)
|
|
# (vbox)
|
|
if self.toolBar is not None and self.toolBarPos == 1:
|
|
self.workContainer1.add_widget(self.toolBar)
|
|
self.workContainer1.add_widget(self.workContainer)
|
|
if self.toolBar is not None and self.toolBarPos == 2:
|
|
self.workContainer1.add_widget(self.toolBar)
|
|
# (workcontainer)
|
|
for w in self.workStack.items:
|
|
self.workContainer.add_widget(w[1])
|
|
|
|
def rebuildContainer(self):
|
|
self.removeContainer()
|
|
self.buildContainer()
|
|
|
|
def pushWork(self, key, widget):
|
|
if (widget):
|
|
self.workStack.push(key, widget)
|
|
self.rebuildContainer()
|
|
|
|
def popWork(self, key):
|
|
w = None
|
|
if self.workStack.size() > 0:
|
|
w = self.workStack.pop(key)
|
|
self.rebuildContainer()
|
|
return w
|
|
|
|
def setWork(self, key, widget):
|
|
self.pushWork(key, widget)
|
|
|
|
def getWork(self, key):
|
|
return self.workStack.peek(key)
|
|
|
|
def processAndroidBack(self):
|
|
ret = False
|
|
# try to close currently open popup windows, one by one
|
|
r = range(len(self.workStack.items))
|
|
rr = reversed(r)
|
|
for i in rr:
|
|
t = self.workStack.items[i]
|
|
# print("stackkey: %s" % str(t[0]))
|
|
# print("stackitem: %s" % str(t[1]))
|
|
if t[0] == 'playground':
|
|
pass
|
|
else:
|
|
if isinstance(t[1], LTopLevel):
|
|
ret = t[1].processAndroidBack()
|
|
if not ret:
|
|
self.popWork(t[0])
|
|
ret = True
|
|
if ret:
|
|
break
|
|
return ret
|
|
|
|
# =============================================================================
|
|
|
|
|
|
class LApp(App):
|
|
|
|
# Handling of android return key
|
|
def key_input(self, window, key, scancode, codepoint, modifier):
|
|
if key == 27:
|
|
# Back key of Android.
|
|
lapp = App.get_running_app()
|
|
app = lapp.app
|
|
if app is None:
|
|
return False # delegate
|
|
|
|
# redirect to mainwindow to close popups, tree nodes
|
|
# and html pages.
|
|
if (self.mainWindow.processAndroidBack()):
|
|
return True # consumed
|
|
|
|
# redirect to game to undo last step
|
|
app.menubar.mUndo()
|
|
return True # consumed
|
|
else:
|
|
return False # delegate
|
|
|
|
def __init__(self):
|
|
super(LApp, self).__init__()
|
|
|
|
# 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)
|
|
Cache.append('LAppCache', 'mainApp', self, timeout=0)
|
|
self.startCode = 0
|
|
|
|
# Es gibt hier offensichtlich nur einen Bilschirm mit Höhe und Breite.
|
|
# Alles andere stellt das Betriebssystem zur Verfügung. Wir wissen auch
|
|
# nicht, wie das Gerät gerade orientiert ist, ist nicht unsere Sache.
|
|
# Alles was wir tun können ist Höhe und Breite zu verfolgen, sobald wir
|
|
# dazu informiert werden. (Android informiert leider nicht immer, wenn
|
|
# es nötig wäre).
|
|
# Update:
|
|
# Nachdem im Manifest nun steht 'configChange=...|screenSize' bekommen
|
|
# wir auch nach dem on_resume ein Signal.
|
|
|
|
def delayedRebuild(self, dt):
|
|
logging.info("LApp: delayedRebuild")
|
|
self.mainWindow.rebuildContainer()
|
|
|
|
def makeDelayedRebuild(self):
|
|
def delayedRebuild(dt):
|
|
# Clock.schedule_once(self.delayedRebuild, 0.01)
|
|
Clock.schedule_once(self.delayedRebuild, 0.5)
|
|
return delayedRebuild
|
|
|
|
def doSize(self, obj, val):
|
|
mval = self.mainWindow.size
|
|
if (val[0] != mval[0] and val[1] != mval[1]):
|
|
logging.info("LApp: size changed %s - %s (%s)" % (obj, val, mval))
|
|
Clock.schedule_once(self.makeDelayedRebuild(), 0.2)
|
|
pass
|
|
|
|
def on_start(self):
|
|
logging.info('mw = %s, w = %s' % (self.mainWindow, Window))
|
|
|
|
Window.bind(on_keyboard=self.key_input)
|
|
Window.bind(size=self.doSize)
|
|
|
|
if self.startCode > 0:
|
|
logging.info("LApp: on_start fails")
|
|
return
|
|
|
|
logging.info("LApp: on_start")
|
|
self.mainloop = self.app.mainproc() # Einrichten
|
|
self.mainloop.send(None) # Spielprozess starten
|
|
logging.info("LApp: on_start processed")
|
|
# Android: Request missing android permissions.
|
|
requestStoragePerm()
|
|
|
|
def on_stop(self):
|
|
# Achtung wird u.U. 2 mal aufgerufen !!!
|
|
logging.info("LApp: on_stop")
|
|
if self.startCode > 0:
|
|
return
|
|
# lapp: erweiterte klasse dieser (mit pysolfc app members).
|
|
lapp = App.get_running_app()
|
|
lapp.app.menubar.mHoldAndQuit()
|
|
|
|
def on_pause(self):
|
|
logging.info("LApp: on_pause")
|
|
# return True: wenn wir wirklich in pause gehen. Dann wird auch
|
|
# resume aufgerufen falls die app wieder aktiviert wird.
|
|
# return False: app wird gestoppt (on_stop wird aufgerufen)
|
|
if self.startCode > 0:
|
|
return False
|
|
|
|
pauseSupport = True
|
|
# True ist die bessere Variante.
|
|
|
|
lapp = App.get_running_app()
|
|
app = lapp.app
|
|
if app is None:
|
|
return
|
|
|
|
logging.info("LApp: on_pause - pause on")
|
|
# set pause
|
|
if not app.game.pause:
|
|
app.game.doPause()
|
|
|
|
logging.info("LApp: on_pause - savegame")
|
|
# save game
|
|
try:
|
|
app.game.gstats.holded = 1
|
|
app.game._saveGame(app.fn.holdgame)
|
|
app.opt.game_holded = app.game.id
|
|
app.opt.last_gameid = app.game.id
|
|
except Exception:
|
|
traceback.print_exc()
|
|
pass
|
|
# save options
|
|
try:
|
|
app.saveOptions()
|
|
except Exception:
|
|
traceback.print_exc()
|
|
pass
|
|
# save statistics
|
|
try:
|
|
app.saveStatistics()
|
|
except Exception:
|
|
traceback.print_exc()
|
|
pass
|
|
logging.info("LApp: on_pause - gamesaved")
|
|
|
|
logging.info("LApp: on_pause, Window.size=%s" % str(Window.size))
|
|
|
|
return pauseSupport
|
|
|
|
def on_resume(self):
|
|
logging.info("LApp: on_resume")
|
|
|
|
lapp = App.get_running_app()
|
|
app = lapp.app
|
|
if app is None:
|
|
return
|
|
|
|
AndroidScreenRotation.unlock()
|
|
|
|
so = get_screen_ori()
|
|
go = so # flake8: F841 nonsense!
|
|
so = go
|
|
logging.info("LApp: on_resume, Window.size=%s" % str(Window.size))
|
|
# ANM:
|
|
# kivy.core.window.Window hat hier u.U. eine falsche dimension
|
|
# und unterscheidet sich vom display (-> in get_screen_ori).
|
|
# Eine korrektur der Parameter von Window kann hier wie skizziert
|
|
# durchgeführt werden und führt auch zu den korrekten 'on_size'
|
|
# Notifikationen. Allerdings wird später (nach diesem Aufruf)
|
|
# eine weitere Notifikation erhalten, welche das Fenster u.U.
|
|
# wieder falsch aufstellt. (woher kommt die und warum ist sie
|
|
# oft falsch ?)
|
|
|
|
if app.game.pause:
|
|
Clock.schedule_once(self.makeEndPauseCmd(app), 3.0)
|
|
|
|
def makeEndPauseCmd(self, app):
|
|
def endPauseCmd(dt):
|
|
if app.game.pause:
|
|
logging.info("LApp: on_resume - pause off")
|
|
app.game.doPause()
|
|
return endPauseCmd
|