Initial version master
authorNeil Smith <neil.git@njae.me.uk>
Tue, 10 May 2016 09:20:27 +0000 (10:20 +0100)
committerNeil Smith <neil.git@njae.me.uk>
Tue, 10 May 2016 09:20:27 +0000 (10:20 +0100)
.buildozer/android/app/main.py [new file with mode: 0644]
.buildozer/android/app/sitecustomize.py [new file with mode: 0644]
.buildozer/android/app/tenraroller.kv [new file with mode: 0644]
.buildozer/android/platform/python-for-android [new submodule]
.buildozer/android/platform/python-for-android-master [new submodule]
.buildozer/state.db [new file with mode: 0644]
.gitignore [new file with mode: 0644]
buildozer.spec [new file with mode: 0644]
main.py [new file with mode: 0644]
tenra-roller.sublime-project [new file with mode: 0644]
tenraroller.kv [new file with mode: 0644]

diff --git a/.buildozer/android/app/main.py b/.buildozer/android/app/main.py
new file mode 100644 (file)
index 0000000..a98e9c5
--- /dev/null
@@ -0,0 +1,113 @@
+from kivy.app import App
+from kivy.uix.widget import Widget
+from kivy.uix.boxlayout import BoxLayout
+from kivy.uix.slider import Slider
+from kivy.uix.textinput import TextInput
+from kivy.uix.label import Label
+from kivy.uix.listview import ListView
+from kivy.properties import NumericProperty, ReferenceListProperty,\
+    ObjectProperty, BoundedNumericProperty
+from kivy.vector import Vector
+from kivy.clock import Clock
+from kivy.adapters.simplelistadapter import SimpleListAdapter
+
+import re
+import random
+
+MAX_DICE = 30
+MAX_SKILL = 6
+
+class SlavedSlider(Slider):
+    pass
+
+class SlavedTextInput(TextInput):
+    pat = re.compile('[^0-9]')
+
+    def insert_text(self, substring, from_undo=False):
+        pat = self.pat
+        s = re.sub(pat, '', substring)
+        return super(SlavedTextInput, self).insert_text(s, from_undo=from_undo)  
+
+class TenraRoller(BoxLayout):
+
+    max_dice = NumericProperty(MAX_DICE)
+    max_skill = NumericProperty(MAX_SKILL)
+
+    dice_slider = ObjectProperty(None)
+    dice_text = ObjectProperty(None)
+    dice_value = BoundedNumericProperty(int(MAX_DICE / 2), min=1, max=MAX_DICE)
+
+    skill_slider = ObjectProperty(None)
+    skill_text = ObjectProperty(None)
+    skill_value = BoundedNumericProperty(int(MAX_SKILL / 2), min=1, max=MAX_SKILL)
+
+    result_label = ObjectProperty(None)
+    previous_results = ObjectProperty(None)
+
+    last_results_texts = []
+    previous_results_la = SimpleListAdapter(data=last_results_texts, 
+                          cls=Label)
+
+
+    def slider_value_change(self, myid):
+        print("Slider {} changed to {}".format(myid, myid.value))
+        if myid == self.dice_slider:
+            self.dice_value = int(myid.value)
+            self.dice_text.text = str(self.dice_value)
+        if myid == self.skill_slider:
+            self.skill_value = int(myid.value)
+            self.skill_text.text = str(self.skill_value)
+
+    def text_value_change(self, myid):
+        print("Text {} changed to {}".format(myid, myid.text))
+        try:
+            if myid == self.skill_text:
+                self.skill_value = int(myid.text)
+                self.skill_slider.value = self.skill_value
+            if myid == self.dice_text:
+                self.dice_value = int(myid.text)
+                self.dice_slider.value = self.dice_value
+        except ValueError:
+            myid.background_color = [1, 0.5, 0.5, 1]
+        else:
+            myid.background_color = [1, 1, 1, 1]
+
+        # if myid == self.dice_text:
+        #     self.dice_value = int(myid.text)
+        #     self.dice_slider.value = self.dice_value
+        # if myid == self.skill_text:
+        #     try:
+        #         self.skill_value = int(myid.text)
+        #     except ValueError:
+        #         myid.background_color = [1, 0.5, 0.5, 1]
+        #     else:
+        #         myid.background_color = [1, 1, 1, 1]
+        #     self.skill_slider.value = self.skill_value
+
+
+    def roll_dice(self):
+        successes = 0
+        for i in range(self.dice_value):
+            if random.randint(1, 6) <= self.skill_value:
+                successes += 1
+        self.last_results_texts = [self.result_label.text] + self.last_results_texts
+        self.result_label.text = 'Rolled {dice}({skill}) for {succ} successes'.format(dice=self.dice_value, skill=self.skill_value, 
+                succ=successes)
+        self.previous_results_la.data = self.last_results_texts
+
+
+# def slider_value_change(slider, value):
+#     print("Slider {} changed to {}".format(slider, value))
+
+class TenraRollerApp(App):
+    # dice_slider = ObjectProperty(None)
+    # dice_text = ObjectProperty(None)
+    # dice_value = BoundedNumericProperty(10, min=1, max=30)
+
+    def build(self):
+        roller = TenraRoller(max_dice=MAX_DICE, max_skill=MAX_SKILL)
+        return roller
+
+
+if __name__ == '__main__':
+    TenraRollerApp().run()
diff --git a/.buildozer/android/app/sitecustomize.py b/.buildozer/android/app/sitecustomize.py
new file mode 100644 (file)
index 0000000..6650864
--- /dev/null
@@ -0,0 +1,3 @@
+from os.path import join, dirname
+import sys
+sys.path.append(join(dirname(__file__), '_applibs'))
diff --git a/.buildozer/android/app/tenraroller.kv b/.buildozer/android/app/tenraroller.kv
new file mode 100644 (file)
index 0000000..62f9462
--- /dev/null
@@ -0,0 +1,72 @@
+<SlavedSlider>:
+
+<SlavedTextInput>:
+
+<TenraRoller>:
+    dice_slider: dice_slider
+    dice_text: dice_text
+    skill_slider: skill_slider
+    skill_text: skill_text
+
+    roll_button: roll_button
+    result_label: result_label
+    previous_results: previous_results
+
+    BoxLayout:
+        orientation: 'vertical'
+        spacing: 10
+        size_hint: 1, 1
+
+        BoxLayout:
+            orientation: 'horizontal'
+            spacing: 10
+            center_x: root.center_x
+            width: root.width
+            Label:
+                text: "Dice"
+            SlavedSlider:
+                id: dice_slider
+                orientation: 'horizontal'
+                min: 1
+                max: root.max_dice
+                step: 1
+                value: int(root.max_dice / 2)
+                on_value: root.slider_value_change(dice_slider)
+            SlavedTextInput:
+                id: dice_text
+                multiline: False
+                on_text: root.text_value_change(dice_text)
+                text: str(int(root.max_dice / 2))
+
+        BoxLayout:
+            orientation: 'horizontal'
+            spacing: 10
+            width: root.width
+            Label:
+                text: "Skill"
+            SlavedSlider:
+                id: skill_slider
+                orientation: 'horizontal'
+                min: 1
+                max: root.max_skill
+                step: 1
+                value: int(root.max_skill / 2)
+                on_value: root.slider_value_change(skill_slider)
+            SlavedTextInput:
+                id: skill_text
+                multiline: False
+                on_text: root.text_value_change(skill_text)
+                text: str(int(root.max_skill / 2))
+        
+        Button:
+            id: roll_button
+            text: "Roll"
+            on_press: root.roll_dice()
+
+        Label:
+            id: result_label
+            text: "No result"
+
+        ListView:
+            id: previous_results
+            adapter: root.previous_results_la
diff --git a/.buildozer/android/platform/python-for-android b/.buildozer/android/platform/python-for-android
new file mode 160000 (submodule)
index 0000000..5674bce
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 5674bced02508dcaa75c798706ea05b7b45eea34
diff --git a/.buildozer/android/platform/python-for-android-master b/.buildozer/android/platform/python-for-android-master
new file mode 160000 (submodule)
index 0000000..f343c14
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit f343c147696e6093204f4d0614480636ec732d85
diff --git a/.buildozer/state.db b/.buildozer/state.db
new file mode 100644 (file)
index 0000000..8e12ebf
--- /dev/null
@@ -0,0 +1 @@
+{"android:latestmode": "debug", "android:sdk_installation": ["19", "9", "9c", "/home/neil/.buildozer/android/platform/android-sdk-20", "/home/neil/.buildozer/android/platform/android-ndk-r9c"], "cache.gardenlibs": "", "android:latestapk": "TBZRoller-0.1-debug.apk", "android:available_permissions": ["ACCESS_CHECKIN_PROPERTIES", "ACCESS_COARSE_LOCATION", "ACCESS_FINE_LOCATION", "ACCESS_LOCATION_EXTRA_COMMANDS", "ACCESS_MOCK_LOCATION", "ACCESS_NETWORK_STATE", "ACCESS_SURFACE_FLINGER", "ACCESS_WIFI_STATE", "ACCOUNT_MANAGER", "ADD_SYSTEM_SERVICE", "ADD_VOICEMAIL", "AUTHENTICATE_ACCOUNTS", "BATTERY_STATS", "BIND_ACCESSIBILITY_SERVICE", "BIND_APPWIDGET", "BIND_DEVICE_ADMIN", "BIND_INPUT_METHOD", "BIND_NFC_SERVICE", "BIND_NOTIFICATION_LISTENER_SERVICE", "BIND_PRINT_SERVICE", "BIND_REMOTEVIEWS", "BIND_TEXT_SERVICE", "BIND_VPN_SERVICE", "BIND_WALLPAPER", "BLUETOOTH", "BLUETOOTH_ADMIN", "BLUETOOTH_PRIVILEGED", "BRICK", "BROADCAST_PACKAGE_REMOVED", "BROADCAST_SMS", "BROADCAST_STICKY", "BROADCAST_WAP_PUSH", "CALL_PHONE", "CALL_PRIVILEGED", "CAMERA", "CAPTURE_AUDIO_OUTPUT", "CAPTURE_SECURE_VIDEO_OUTPUT", "CAPTURE_VIDEO_OUTPUT", "CHANGE_COMPONENT_ENABLED_STATE", "CHANGE_CONFIGURATION", "CHANGE_NETWORK_STATE", "CHANGE_WIFI_MULTICAST_STATE", "CHANGE_WIFI_STATE", "CLEAR_APP_CACHE", "CLEAR_APP_USER_DATA", "CONTROL_LOCATION_UPDATES", "DELETE_CACHE_FILES", "DELETE_PACKAGES", "DEVICE_POWER", "DIAGNOSTIC", "DISABLE_KEYGUARD", "DUMP", "EXPAND_STATUS_BAR", "FACTORY_TEST", "FLASHLIGHT", "FORCE_BACK", "FOTA_UPDATE", "GET_ACCOUNTS", "GET_PACKAGE_SIZE", "GET_TASKS", "GET_TOP_ACTIVITY_INFO", "GLOBAL_SEARCH", "HARDWARE_TEST", "INJECT_EVENTS", "INSTALL_LOCATION_PROVIDER", "INSTALL_PACKAGES", "INSTALL_SHORTCUT", "INTERNAL_SYSTEM_WINDOW", "INTERNET", "KILL_BACKGROUND_PROCESSES", "LOCATION_HARDWARE", "MANAGE_ACCOUNTS", "MANAGE_APP_TOKENS", "MANAGE_DOCUMENTS", "MASTER_CLEAR", "MEDIA_CONTENT_CONTROL", "MODIFY_AUDIO_SETTINGS", "MODIFY_PHONE_STATE", "MOUNT_FORMAT_FILESYSTEMS", "MOUNT_UNMOUNT_FILESYSTEMS", "NFC", "PERSISTENT_ACTIVITY", "PROCESS_OUTGOING_CALLS", "READ_CALENDAR", "READ_CALL_LOG", "READ_CONTACTS", "READ_EXTERNAL_STORAGE", "READ_FRAME_BUFFER", "READ_HISTORY_BOOKMARKS", "READ_INPUT_STATE", "READ_LOGS", "READ_OWNER_DATA", "READ_PHONE_STATE", "READ_PROFILE", "READ_SMS", "READ_SOCIAL_STREAM", "READ_SYNC_SETTINGS", "READ_SYNC_STATS", "READ_USER_DICTIONARY", "REBOOT", "RECEIVE_BOOT_COMPLETED", "RECEIVE_MMS", "RECEIVE_SMS", "RECEIVE_WAP_PUSH", "RECORD_AUDIO", "REORDER_TASKS", "RESTART_PACKAGES", "SEND_RESPOND_VIA_MESSAGE", "SEND_SMS", "SET_ACTIVITY_WATCHER", "SET_ALARM", "SET_ALWAYS_FINISH", "SET_ANIMATION_SCALE", "SET_DEBUG_APP", "SET_ORIENTATION", "SET_POINTER_SPEED", "SET_PREFERRED_APPLICATIONS", "SET_PROCESS_FOREGROUND", "SET_PROCESS_LIMIT", "SET_TIME", "SET_TIME_ZONE", "SET_WALLPAPER", "SET_WALLPAPER_HINTS", "SIGNAL_PERSISTENT_PROCESSES", "STATUS_BAR", "SUBSCRIBED_FEEDS_READ", "SUBSCRIBED_FEEDS_WRITE", "SYSTEM_ALERT_WINDOW", "TRANSMIT_IR", "UNINSTALL_SHORTCUT", "UPDATE_DEVICE_STATS", "USE_CREDENTIALS", "USE_SIP", "VIBRATE", "WAKE_LOCK", "WRITE_APN_SETTINGS", "WRITE_CALENDAR", "WRITE_CALL_LOG", "WRITE_CONTACTS", "WRITE_EXTERNAL_STORAGE", "WRITE_GSERVICES", "WRITE_HISTORY_BOOKMARKS", "WRITE_OWNER_DATA", "WRITE_PROFILE", "WRITE_SECURE_SETTINGS", "WRITE_SETTINGS", "WRITE_SMS", "WRITE_SOCIAL_STREAM", "WRITE_SYNC_SETTINGS", "WRITE_USER_DICTIONARY"], "cache.applibs": [], "android:available_permissions_sdk": "20", "android.requirements": ["kivy"], "cache.build_id": "3", "android.requirements.source": {}}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..0546add
--- /dev/null
@@ -0,0 +1,45 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+__pycache__
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+
+# IPython
+.ipynb*
+
+# Sublime text
+*.sublime-workspace
+
+# Logs
+*.log
diff --git a/buildozer.spec b/buildozer.spec
new file mode 100644 (file)
index 0000000..0b0e3c7
--- /dev/null
@@ -0,0 +1,227 @@
+[app]
+
+# (str) Title of your application
+title = TBZ Roller
+
+# (str) Package name
+package.name = tbzroller
+
+# (str) Package domain (needed for android/ios packaging)
+package.domain = uk.me.njae
+
+# (str) Source code where the main.py live
+source.dir = .
+
+# (list) Source files to include (let empty to include all the files)
+source.include_exts = py,png,jpg,kv,atlas
+
+# (list) List of inclusions using pattern matching
+#source.include_patterns = assets/*,images/*.png
+
+# (list) Source files to exclude (let empty to not exclude anything)
+#source.exclude_exts = spec
+
+# (list) List of directory to exclude (let empty to not exclude anything)
+#source.exclude_dirs = tests, bin
+
+# (list) List of exclusions using pattern matching
+#source.exclude_patterns = license,images/*/*.jpg
+
+# (str) Application versioning (method 1)
+version = 0.1
+
+# (str) Application versioning (method 2)
+# version.regex = __version__ = ['"](.*)['"]
+# version.filename = %(source.dir)s/main.py
+
+# (list) Application requirements
+# comma seperated e.g. requirements = sqlite3,kivy
+requirements = kivy
+
+# (str) Custom source folders for requirements
+# Sets custom source for any requirements with recipes
+# requirements.source.kivy = ../../kivy
+
+# (list) Garden requirements
+#garden_requirements =
+
+# (str) Presplash of the application
+#presplash.filename = %(source.dir)s/data/presplash.png
+
+# (str) Icon of the application
+#icon.filename = %(source.dir)s/data/icon.png
+
+# (str) Supported orientation (one of landscape, portrait or all)
+orientation = landscape
+
+# (list) List of service to declare
+#services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY
+
+#
+# OSX Specific
+#
+
+#
+# author = © Copyright Info
+
+#
+# Android specific
+#
+
+# (bool) Indicate if the application should be fullscreen or not
+fullscreen = 1
+
+# (list) Permissions
+#android.permissions = INTERNET
+
+# (int) Android API to use
+#android.api = 19
+
+# (int) Minimum API required
+#android.minapi = 9
+
+# (int) Android SDK version to use
+#android.sdk = 20
+
+# (str) Android NDK version to use
+#android.ndk = 9c
+
+# (bool) Use --private data storage (True) or --dir public storage (False)
+#android.private_storage = True
+
+# (str) Android NDK directory (if empty, it will be automatically downloaded.)
+#android.ndk_path =
+
+# (str) Android SDK directory (if empty, it will be automatically downloaded.)
+#android.sdk_path =
+
+# (str) ANT directory (if empty, it will be automatically downloaded.)
+#android.ant_path =
+
+# (str) python-for-android git clone directory (if empty, it will be automatically cloned from github)
+#android.p4a_dir =
+
+# (list) python-for-android whitelist
+#android.p4a_whitelist =
+
+# (bool) If True, then skip trying to update the Android sdk
+# This can be useful to avoid excess Internet downloads or save time
+# when an update is due and you just want to test/build your package
+# android.skip_update = False
+
+# (str) Android entry point, default is ok for Kivy-based app
+#android.entrypoint = org.renpy.android.PythonActivity
+
+# (list) List of Java .jar files to add to the libs so that pyjnius can access
+# their classes. Don't add jars that you do not need, since extra jars can slow
+# down the build process. Allows wildcards matching, for example:
+# OUYA-ODK/libs/*.jar
+#android.add_jars = foo.jar,bar.jar,path/to/more/*.jar
+
+# (list) List of Java files to add to the android project (can be java or a
+# directory containing the files)
+#android.add_src =
+
+# (str) python-for-android branch to use, if not master, useful to try
+# not yet merged features.
+#android.branch = master
+
+# (str) OUYA Console category. Should be one of GAME or APP
+# If you leave this blank, OUYA support will not be enabled
+#android.ouya.category = GAME
+
+# (str) Filename of OUYA Console icon. It must be a 732x412 png image.
+#android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png
+
+# (str) XML file to include as an intent filters in <activity> tag
+#android.manifest.intent_filters =
+
+# (list) Android additionnal libraries to copy into libs/armeabi
+#android.add_libs_armeabi = libs/android/*.so
+#android.add_libs_armeabi_v7a = libs/android-v7/*.so
+#android.add_libs_x86 = libs/android-x86/*.so
+#android.add_libs_mips = libs/android-mips/*.so
+
+# (bool) Indicate whether the screen should stay on
+# Don't forget to add the WAKE_LOCK permission if you set this to True
+#android.wakelock = False
+
+# (list) Android application meta-data to set (key=value format)
+#android.meta_data =
+
+# (list) Android library project to add (will be added in the
+# project.properties automatically.)
+#android.library_references =
+
+# (str) Android logcat filters to use
+#android.logcat_filters = *:S python:D
+
+# (bool) Copy library instead of making a libpymodules.so
+#android.copy_libs = 1
+
+#
+# iOS specific
+#
+
+# (str) Path to a custom kivy-ios folder
+#ios.kivy_ios_dir = ../kivy-ios
+
+# (str) Name of the certificate to use for signing the debug version
+# Get a list of available identities: buildozer ios list_identities
+#ios.codesign.debug = "iPhone Developer: <lastname> <firstname> (<hexstring>)"
+
+# (str) Name of the certificate to use for signing the release version
+#ios.codesign.release = %(ios.codesign.debug)s
+
+
+[buildozer]
+
+# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output))
+log_level = 1
+
+# (int) Display warning if buildozer is run as root (0 = False, 1 = True)
+warn_on_root = 1
+
+# (str) Path to build artifact storage, absolute or relative to spec file
+# build_dir = ./.buildozer
+
+# (str) Path to build output (i.e. .apk, .ipa) storage
+# bin_dir = ./bin
+
+#    -----------------------------------------------------------------------------
+#    List as sections
+#
+#    You can define all the "list" as [section:key].
+#    Each line will be considered as a option to the list.
+#    Let's take [app] / source.exclude_patterns.
+#    Instead of doing:
+#
+#[app]
+#source.exclude_patterns = license,data/audio/*.wav,data/images/original/*
+#
+#    This can be translated into:
+#
+#[app:source.exclude_patterns]
+#license
+#data/audio/*.wav
+#data/images/original/*
+#
+
+
+#    -----------------------------------------------------------------------------
+#    Profiles
+#
+#    You can extend section / key with a profile
+#    For example, you want to deploy a demo version of your application without
+#    HD content. You could first change the title to add "(demo)" in the name
+#    and extend the excluded directories to remove the HD content.
+#
+#[app@demo]
+#title = My Application (demo)
+#
+#[app:source.exclude_patterns@demo]
+#images/hd/*
+#
+#    Then, invoke the command line with the "demo" profile:
+#
+#buildozer --profile demo android debug
diff --git a/main.py b/main.py
new file mode 100644 (file)
index 0000000..5e599e2
--- /dev/null
+++ b/main.py
@@ -0,0 +1,117 @@
+from kivy.app import App
+from kivy.uix.widget import Widget
+from kivy.uix.boxlayout import BoxLayout
+from kivy.uix.slider import Slider
+from kivy.uix.textinput import TextInput
+from kivy.uix.label import Label
+from kivy.uix.listview import ListView
+from kivy.properties import NumericProperty, ReferenceListProperty,\
+    ObjectProperty, BoundedNumericProperty, StringProperty
+from kivy.vector import Vector
+from kivy.clock import Clock
+from kivy.adapters.simplelistadapter import SimpleListAdapter
+
+import re
+import random
+
+MAX_DICE = 40
+MAX_SKILL = 5
+INITIAL_RESULT_LABEL = "No result"
+
+class SlavedSlider(Slider):
+    pass
+
+class SlavedTextInput(TextInput):
+    pat = re.compile('[^0-9]')
+
+    def insert_text(self, substring, from_undo=False):
+        pat = self.pat
+        s = re.sub(pat, '', substring)
+        return super(SlavedTextInput, self).insert_text(s, from_undo=from_undo)  
+
+class PaddedLabel(Label):
+    line_height = 2
+
+class TenraRoller(BoxLayout):
+
+    max_dice = NumericProperty(MAX_DICE)
+    max_skill = NumericProperty(MAX_SKILL)
+    initial_result_label_text = StringProperty(INITIAL_RESULT_LABEL)
+
+    dice_slider = ObjectProperty(None)
+    dice_text = ObjectProperty(None)
+    dice_value = BoundedNumericProperty(int(MAX_DICE / 2), min=1, max=MAX_DICE)
+
+    skill_slider = ObjectProperty(None)
+    skill_text = ObjectProperty(None)
+    skill_value = BoundedNumericProperty(int(MAX_SKILL / 2), min=1, max=MAX_SKILL)
+
+    result_label = ObjectProperty(None)
+    previous_results = ObjectProperty(None)
+
+    previous_results_la = SimpleListAdapter(data=[], 
+                          cls=PaddedLabel)
+
+
+    def slider_value_change(self, myid):
+        print("Slider {} changed to {}".format(myid, myid.value))
+        if myid == self.dice_slider:
+            self.dice_value = int(myid.value)
+            self.dice_text.text = str(self.dice_value)
+        if myid == self.skill_slider:
+            self.skill_value = int(myid.value)
+            self.skill_text.text = str(self.skill_value)
+
+    def text_value_change(self, myid):
+        print("Text {} changed to {}".format(myid, myid.text))
+        try:
+            if myid == self.skill_text:
+                self.skill_value = int(myid.text)
+                self.skill_slider.value = self.skill_value
+            if myid == self.dice_text:
+                self.dice_value = int(myid.text)
+                self.dice_slider.value = self.dice_value
+        except ValueError:
+            myid.background_color = [1, 0.5, 0.5, 1]
+        else:
+            myid.background_color = [1, 1, 1, 1]
+
+        # if myid == self.dice_text:
+        #     self.dice_value = int(myid.text)
+        #     self.dice_slider.value = self.dice_value
+        # if myid == self.skill_text:
+        #     try:
+        #         self.skill_value = int(myid.text)
+        #     except ValueError:
+        #         myid.background_color = [1, 0.5, 0.5, 1]
+        #     else:
+        #         myid.background_color = [1, 1, 1, 1]
+        #     self.skill_slider.value = self.skill_value
+
+
+    def roll_dice(self):
+        successes = 0
+        for i in range(self.dice_value):
+            if random.randint(1, 6) <= self.skill_value:
+                successes += 1
+        if self.result_label.text != INITIAL_RESULT_LABEL:
+            self.previous_results_la.data = [self.result_label.text] + self.previous_results_la.data
+        self.result_label.text = 'Rolled {dice}({skill}) for {succ} successes'.format(dice=self.dice_value, skill=self.skill_value, 
+                succ=successes)
+
+
+# def slider_value_change(slider, value):
+#     print("Slider {} changed to {}".format(slider, value))
+
+class TenraRollerApp(App):
+    # dice_slider = ObjectProperty(None)
+    # dice_text = ObjectProperty(None)
+    # dice_value = BoundedNumericProperty(10, min=1, max=30)
+
+    def build(self):
+        roller = TenraRoller()
+        return roller
+
+
+if __name__ == '__main__':
+    TenraRollerApp().run()
diff --git a/tenra-roller.sublime-project b/tenra-roller.sublime-project
new file mode 100644 (file)
index 0000000..24db303
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "folders":
+       [
+               {
+                       "path": "."
+               }
+       ]
+}
diff --git a/tenraroller.kv b/tenraroller.kv
new file mode 100644 (file)
index 0000000..b2b0067
--- /dev/null
@@ -0,0 +1,71 @@
+<SlavedSlider>:
+    orientation: 'horizontal'
+    min: 1
+    step: 1
+
+<SlavedTextInput>:
+    padding: [5, ( self.height - self.line_height ) / 2]
+    multiline: False
+
+<TenraRoller>:
+    dice_slider: dice_slider
+    dice_text: dice_text
+    skill_slider: skill_slider
+    skill_text: skill_text
+
+    roll_button: roll_button
+    result_label: result_label
+    previous_results: previous_results
+
+    BoxLayout:
+        orientation: 'vertical'
+        spacing: 10
+
+        BoxLayout:
+            orientation: 'horizontal'
+            spacing: 10
+            center_x: root.center_x
+            width: root.width
+            Label:
+                text: "Dice"
+            SlavedSlider:
+                id: dice_slider
+                max: root.max_dice
+                value: root.dice_value
+                on_value: root.slider_value_change(dice_slider)
+            SlavedTextInput:
+                id: dice_text
+                text: str(root.dice_value)
+                on_text: root.text_value_change(dice_text)
+
+
+        BoxLayout:
+            orientation: 'horizontal'
+            spacing: 10
+            width: root.width
+            Label:
+                text: "Skill"
+            SlavedSlider:
+                id: skill_slider
+                max: root.max_skill
+                value: root.skill_value
+                on_value: root.slider_value_change(skill_slider)
+            SlavedTextInput:
+                id: skill_text
+                on_text: root.text_value_change(skill_text)
+                text: str(root.skill_value)
+
+        
+        Button:
+            id: roll_button
+            text: "Roll"
+            on_press: root.roll_dice()
+
+        Label:
+            id: result_label
+            bold: True
+            text: root.initial_result_label_text
+
+        ListView:
+            id: previous_results
+            adapter: root.previous_results_la