Source code for elide.statcfg

# This file is part of Elide, frontend to Lisien, a framework for life simulation games.
# Copyright (c) Zachary Spector, public@zacharyspector.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3.
#
# 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
from kivy.app import App
from kivy.clock import Clock
from kivy.properties import (
	DictProperty,
	NumericProperty,
	ObjectProperty,
	StringProperty,
)
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
from kivy.uix.screenmanager import Screen
from kivy.uix.textinput import TextInput

from .statlist import BaseStatListView
from .util import load_string_once


[docs] class FloatInput(TextInput):
[docs] def insert_text(self, s, from_undo=False): return super().insert_text( "".join(c for c in s if c in "0123456789."), from_undo )
[docs] class ControlTypePicker(Button): app = ObjectProperty() key = ObjectProperty() mainbutton = ObjectProperty() dropdown = ObjectProperty() set_control = ObjectProperty() def __init__(self, **kwargs): super().__init__(**kwargs) self.build() def set_value(self, k, v): if v is None: del self.app.selected_proxy[k] else: self.app.selected_proxy[k] = v def build(self, *_): if None in (self.key, self.set_control): Clock.schedule_once(self.build, 0) return self.mainbutton = None self.dropdown = None self.dropdown = DropDown() self.dropdown.bind( on_select=lambda instance, x: self.set_control(self.key, x) ) readoutbut = Button( text="readout", size_hint_y=None, height=self.height, background_color=(0.7, 0.7, 0.7, 1), ) readoutbut.bind( on_release=lambda instance: self.dropdown.select("readout") ) self.dropdown.add_widget(readoutbut) textinbut = Button( text="textinput", size_hint_y=None, height=self.height, background_color=(0.7, 0.7, 0.7, 1), ) textinbut.bind( on_release=lambda instance: self.dropdown.select("textinput") ) self.dropdown.add_widget(textinbut) togbut = Button( text="togglebutton", size_hint_y=None, height=self.height, background_color=(0.7, 0.7, 0.7, 1), ) togbut.bind( on_release=lambda instance: self.dropdown.select("togglebutton") ) self.dropdown.add_widget(togbut) sliderbut = Button( text="slider", size_hint_y=None, height=self.height, background_color=(0.7, 0.7, 0.7, 1), ) sliderbut.bind( on_release=lambda instance: self.dropdown.select("slider") ) self.dropdown.add_widget(sliderbut) self.bind(on_release=self.dropdown.open)
[docs] class ConfigListItemToggleButton(BoxLayout): true_text = StringProperty("0") false_text = StringProperty("1") def set_true_text(self, *_): self.parent.set_config( self.parent.key, "true_text", self.ids.truetext.text ) self.true_text = self.ids.truetext.text def set_false_text(self, *_): self.parent.set_config( self.parent.key, "false_text", self.ids.falsetext.text )
[docs] class ConfigListItemSlider(BoxLayout): min = NumericProperty(0.0) max = NumericProperty(1.0) def set_min(self, *_): minn = float(self.ids.minimum.text) try: self.parent.set_config(self.parent.key, "min", minn) self.min = minn except ValueError: self.ids.minimum.text = "" def set_max(self, *_): maxx = float(self.ids.maximum.text) try: self.parent.set_config(self.parent.key, "max", maxx) self.max = maxx self.ids.maximum.hint_text = str(maxx) except ValueError: self.ids.maximum.text = ""
[docs] class ConfigListItemCustomizer(BoxLayout): key = ObjectProperty() control = StringProperty() config = DictProperty() set_config = ObjectProperty() def on_control(self, *_): self.clear_widgets() if self.control == "togglebutton": if ( "true_text" not in self.config or "false_text" not in self.config ): Clock.schedule_once(self.on_control, 0) return wid = self._toggle = ConfigListItemToggleButton( true_text=self.config["true_text"], false_text=self.config["false_text"], ) self.add_widget(wid) elif self.control == "slider": if "min" not in self.config or "max" not in self.config: Clock.schedule_once(self.on_control, 0) return wid = self._slider = ConfigListItemSlider( min=self.config["min"], max=self.config["max"] ) self.add_widget(wid) def on_config(self, *_): if hasattr(self, "_toggle"): if "true_text" in self.config: self._toggle.true_text = self.config["true_text"] if "false_text" in self.config: self._toggle.false_text = self.config["false_text"] if hasattr(self, "_slider"): if "min" in self.config: self._slider.min = self.config["min"] if "max" in self.config: self._slider.max = self.config["max"]
[docs] class ConfigListItem(BoxLayout): key = ObjectProperty() config = DictProperty() set_control = ObjectProperty() set_config = ObjectProperty() deleter = ObjectProperty()
[docs] class StatListViewConfigurator(BaseStatListView): statlist = ObjectProperty() _key_cfg_setters = DictProperty() _val_text_setters = DictProperty() _control_wids = DictProperty() def set_control(self, key, value): config = self.proxy.get("_config", {}) if value == "slider": if "min" not in config: self.set_config(key, "min", 0.0) if "max" not in config: self.set_config(key, "max", 1.0) elif value == "togglebutton": if "true_text" not in config: self.set_config(key, "true_text", "1") if "false_text" not in config: self.set_config(key, "false_text", "0") self.set_config(key, "control", value)
[docs] def munge(self, k, v): # makes ConfigListItem ret = super().munge(k, v) ret["deleter"] = self.del_key ret["set_control"] = self.set_control ret["set_config"] = self.set_config return ret
[docs] class StatScreen(Screen): statlist = ObjectProperty() statcfg = ObjectProperty() toggle = ObjectProperty() proxy = ObjectProperty() @property def engine(self): return App.get_running_app().engine
[docs] def new_stat(self): """Look at the key and value that the user has entered into the stat configurator, and set them on the currently selected entity. """ key = self.ids.newstatkey.text value = self.ids.newstatval.text if not (key and value): # TODO implement some feedback to the effect that # you need to enter things return try: self.proxy[key] = self.engine.unpack(value) except (TypeError, ValueError): self.proxy[key] = value self.ids.newstatkey.text = "" self.ids.newstatval.text = ""
load_string_once(""" <ConfigListItemCustomization>: pos_hint: {'x': 0, 'y': 0} <ConfigListItemToggleButton>: Label: text: 'True text:' TextInput: id: truetext hint_text: root.true_text on_text_validate: root.set_true_text() Label: text: 'False text:' TextInput: id: falsetext hint_text: root.false_text on_text_validate: root.set_false_text() <ConfigListItemSlider>: Label: text: 'Minimum:' TextInput: id: minimum hint_text: str(root.min) multiline: False on_text_validate: root.set_min() Label: text: 'Maximum:' TextInput: id: maximum hint_text: str(root.max) multiline: False on_text_validate: root.set_max() <ConfigListItem>: height: 30 Button: size_hint_x: 0.4 / 3 text: 'del' on_release: root.deleter(root.key) Label: size_hint_x: 0.4 / 3 text: str(root.key) ControlTypePicker: size_hint_x: 0.4 / 3 key: root.key set_control: root.set_control text: root.config['control'] if 'control' in root.config else 'readout' ConfigListItemCustomizer: size_hint_x: 0.6 control: root.config['control'] if 'control' in root.config else 'readout' config: root.config key: root.key set_config: root.set_config <StatScreen>: name: 'statcfg' statcfg: cfg BoxLayout: orientation: 'vertical' StatListViewConfigurator: viewclass: 'ConfigListItem' id: cfg app: app proxy: root.proxy statlist: root.statlist size_hint_y: 0.95 RecycleBoxLayout: default_size: None, dp(56) default_size_hint: 1, None size_hint_y: None height: self.minimum_height orientation: 'vertical' BoxLayout: orientation: 'horizontal' size_hint_y: 0.05 TextInput: id: newstatkey multiline: False write_tab: False hint_text: 'New stat' TextInput: id: newstatval multiline: False write_tab: False hint_text: 'Value' Button: id: newstatbut text: '+' on_release: root.new_stat() Button: id: closer text: 'Close' on_release: root.toggle() """)