From 4f626daa79e31e194840c43c789dc394d3d21968 Mon Sep 17 00:00:00 2001 From: Atis Date: Fri, 4 Aug 2017 15:37:44 +0300 Subject: [PATCH] Update 4.00 --- CHANGELOG.md | 20 ++++++++- app_recycleview/behaviors/line_split.py | 1 - app_recycleview/recycleview.py | 10 ++++- kb_system/compat_widgets/menu.py | 22 ++++++---- kb_system/compat_widgets/popup.py | 28 +++++++++++++ kb_system/compat_widgets/scrollview.py | 54 +++++++++++++++++++++++++ kb_system/focus.py | 1 - kb_system/keys.py | 1 + template_app/logs.py | 16 ++++---- template_app/t_app.py | 8 ++-- terminal_widget/plugins/hide.py | 9 +++++ terminal_widget/term_system.py | 4 +- terminal_widget/term_widget.py | 4 +- text_editor/editor.py | 31 ++++++++++---- various_widgets/__init__.py | 0 various_widgets/popup_label.py | 33 +++++++++++++++ version | 2 +- 17 files changed, 208 insertions(+), 36 deletions(-) create mode 100644 kb_system/compat_widgets/popup.py create mode 100644 kb_system/compat_widgets/scrollview.py create mode 100644 terminal_widget/plugins/hide.py create mode 100644 various_widgets/__init__.py create mode 100644 various_widgets/popup_label.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 80dee66..5b85b00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,26 @@ +# 4.00 + - Added AppRecycleView filters property + - Changed AppRecycleView on_key_down method to scroll a page up/down instead of to start or end when page up/down is pressed + - Added AppRecycleView scrolling to start/end with start/end keyboard buttons + - Changed MDMenu on_key_down method to close menu before raising exceptions + - Added new AppPopup kb_system compat widget + - Added new AppScrollView kb_system compat widget + - Added kwargs in logger patch logger functions + - Removed android pan softinput mode in TemplateApp + - Added terminal_widget hide plugin + - Fixed TerminalWidgetSystem type error when text is not a string or unicode + - Added TextEditor text property + - Added various_widgets package with PopupLabel widget + + # 3.10 - Added HoverBehavior improvements from my kivy pr + # 3.00 - Added on_release event dispatch in kb_system compat menu widget - Changed template_app TextInput rule to TerminalWidgetTextInput - - Added print_button_text method for terminal_widget press_button plugin + - Added print_button_text method for terminal_widget press_button plugin - Improved focus behavior integration for for text editor - Added quick_open method for text editor popup @@ -12,7 +28,7 @@ # 2.00 - Added changelog - Changed AppRecycleBoxLayout.open_context_menu method to return what is returned by context_menu_function - - Added AppRecycleView on_key_down method with default keys for scrolling and selecting with keyboard that integrate with kivy_soil kb_system + - Added AppRecycleView on_key_down method with default keys for scrolling and selecting with keyboard that integrate with kb_system - Added AppRecycleView warning when default_height and data dict height is not set - Added modified kivy_md menu widgets with kb_system focus behavior compatability - Fixed FocusBehavior grabbing focus when is_focusable is set to False diff --git a/app_recycleview/behaviors/line_split.py b/app_recycleview/behaviors/line_split.py index c21505a..03e8aa7 100644 --- a/app_recycleview/behaviors/line_split.py +++ b/app_recycleview/behaviors/line_split.py @@ -5,7 +5,6 @@ class LineSplitBehavior(object): split_text_key = StringProperty('text') - split_text_indent = NumericProperty(4) chars_per_line = NumericProperty(100) _unsplit_data = ListProperty() diff --git a/app_recycleview/recycleview.py b/app_recycleview/recycleview.py index dc7e00c..b9643ba 100644 --- a/app_recycleview/recycleview.py +++ b/app_recycleview/recycleview.py @@ -23,6 +23,8 @@ class AppRecycleView(RecycleView): data_full = ListProperty() '''ListProperty that stores all widget data unsorted''' + filters = ListProperty() + def __init__(self, **kwargs): super(AppRecycleView, self).__init__(**kwargs) self.fbind('reverse_sorting', self.update_data_from_filter) @@ -37,8 +39,12 @@ def on_key_down(self, key, modifier): elif key == keys.DOWN: self.children[0].on_arrow_down() elif key == keys.PAGE_UP: - self.scroll_to_start() + self.page_up() elif key == keys.PAGE_DOWN: + self.page_down() + elif key == keys.HOME: + self.scroll_to_start() + elif key == keys.END: self.scroll_to_end() elif key in (keys.MENU, keys.MENU_WIN): drop = self.children[0].open_context_menu() @@ -64,6 +70,8 @@ def update_data_from_filter(self, *args): data = self.sort_data(data, self.reverse_sorting, self.sorting_key) if self.children: self.children[0].on_data_update_sel(len(self.data), len(data)) + for x in self.filters: + data = x(data) self.data = data self.refresh_from_data() diff --git a/kb_system/compat_widgets/menu.py b/kb_system/compat_widgets/menu.py index d9a649f..86e0b05 100644 --- a/kb_system/compat_widgets/menu.py +++ b/kb_system/compat_widgets/menu.py @@ -86,13 +86,21 @@ def on_key_down(self, key, *args): box.on_arrow_down() self.remove_hover() elif key in (keys.RETURN, keys.ENTER): - for x in box.children: - if x.selected: - if not x.disabled: - x.on_press() - x.on_release() - self.dismiss_ctx_menu() - break + e = None + try: + for x in box.children: + if x.selected: + if not x.disabled: + x.on_press() + x.on_release() + break + except Exception as e: + pass + finally: + # Always close menu before raising exception + self.dismiss_ctx_menu() + if e: + raise e elif key == keys.ESC: self.dismiss_ctx_menu() diff --git a/kb_system/compat_widgets/popup.py b/kb_system/compat_widgets/popup.py new file mode 100644 index 0000000..b091fcd --- /dev/null +++ b/kb_system/compat_widgets/popup.py @@ -0,0 +1,28 @@ +from kivy_soil.kb_system.canvas import FocusBehaviorCanvas +from kivy.uix.modalview import ModalView +from kivy_soil import hover_behavior +from kivy_soil.kb_system import keys +from kivy.uix.popup import Popup +from kivy.lang import Builder + + +class AppPopup(hover_behavior.HoverBehavior, FocusBehaviorCanvas, Popup): + '''A special Popup class that integrates with kivy_soil hover_behavior + and increases hover_behavior.min_hover_height when it is opened''' + + hover_height = 20 + grab_keys = [keys.ESC] + + def __init__(self, **kwargs): + super(AppPopup, self).__init__(**kwargs) + self.grab_focus = True + + def open(self): + hover_behavior.min_hover_height = self.hover_height + super(AppPopup, self).open() + self.is_focusable = True + + def dismiss(self): + hover_behavior.min_hover_height = 0 + super(AppPopup, self).dismiss() + self.is_focusable = False diff --git a/kb_system/compat_widgets/scrollview.py b/kb_system/compat_widgets/scrollview.py new file mode 100644 index 0000000..ac1115a --- /dev/null +++ b/kb_system/compat_widgets/scrollview.py @@ -0,0 +1,54 @@ +from kivy.uix.scrollview import ScrollView +from kivy_soil.kb_system import keys + + +class AppScrollView(ScrollView): + def on_key_down(self, key, modifier): + if key == keys.UP: + self.scroll_up_slightly() + elif key == keys.DOWN: + self.scroll_down_slightly() + elif key == keys.PAGE_UP: + self.page_up() + elif key == keys.PAGE_DOWN: + self.page_down() + elif key == keys.HOME: + self.scroll_to_start() + elif key == keys.END: + self.scroll_to_end() + + def scroll_to_start(self): + self.scroll_y = 1.0 + self._update_effect_bounds() + + def scroll_to_end(self): + self.scroll_y = 0.0 + self._update_effect_bounds() + + def scroll_up_slightly(self): + '''Scrolls viewport down by it's height * 0.1''' + scroll = ScrollView.convert_distance_to_scroll( + self, 0, self.height)[1] * 0.1 + self.scroll_y = min(self.scroll_y + scroll, 1.0) + self._update_effect_bounds() + + def scroll_down_slightly(self): + '''Scrolls viewport down by it's height * 0.1''' + scroll = ScrollView.convert_distance_to_scroll( + self, 0, self.height)[1] * 0.1 + self.scroll_y = max(self.scroll_y - scroll, 0.0) + self._update_effect_bounds() + + def page_down(self): + '''Scrolls viewport down by it's height * 0.9''' + scroll = ScrollView.convert_distance_to_scroll( + self, 0, self.height)[1] * 0.9 + self.scroll_y = max(self.scroll_y - scroll, 0.0) + self._update_effect_bounds() + + def page_up(self): + '''Scrolls viewport up by it's height * 0.9''' + scroll = ScrollView.convert_distance_to_scroll( + self, 0, self.height)[1] * 0.9 + self.scroll_y = min(self.scroll_y + scroll, 1.0) + self._update_effect_bounds() diff --git a/kb_system/focus.py b/kb_system/focus.py index dec5303..a26f0d6 100644 --- a/kb_system/focus.py +++ b/kb_system/focus.py @@ -80,7 +80,6 @@ def focus_next(): for fwidget in focus_grab_widgets: if fwidget.is_focusable: grabbed_focus = True - # fwidget = focus_grab_widgets[0] if fwidget.subfocus_widgets: if fwidget.focus: new_focus = fwidget.subfocus_widgets[0] diff --git a/kb_system/keys.py b/kb_system/keys.py index cdf8103..4445650 100644 --- a/kb_system/keys.py +++ b/kb_system/keys.py @@ -1,3 +1,4 @@ +BACKSPACE = 8 TAB = 9 RETURN = 13 diff --git a/template_app/logs.py b/template_app/logs.py index b8a88d4..5f1c27a 100644 --- a/template_app/logs.py +++ b/template_app/logs.py @@ -20,20 +20,20 @@ def add_data(self, text, level): LoggerHistoryProper = LoggerHistoryProper() -def exception(msg, *args): - Logger._exception(msg) +def exception(msg, *args, **kwargs): + Logger._exception(msg, *args, **kwargs) LoggerHistoryProper.add_data(msg, 'exception') -def warning(msg, *args): - Logger._warning(msg) +def warning(msg, *args, **kwargs): + Logger._warning(msg, *args, **kwargs) LoggerHistoryProper.add_data(msg, 'warning') -def error(msg, *args): - Logger._error(msg) +def error(msg, *args, **kwargs): + Logger._error(msg, *args, **kwargs) LoggerHistoryProper.add_data(msg, 'error') -def info(msg, *args): - Logger._info(msg) +def info(msg, *args, **kwargs): + Logger._info(msg, *args, **kwargs) LoggerHistoryProper.add_data(msg, 'info') Logger._exception = Logger.exception diff --git a/template_app/t_app.py b/template_app/t_app.py index 14916a9..1441864 100644 --- a/template_app/t_app.py +++ b/template_app/t_app.py @@ -33,8 +33,6 @@ def __init__(self, **kwargs): Clock.schedule_once(self.init_template_widgets, 0) elif self.use_template_keybinds: self.init_keybinds() - if platform == 'android': - Window.softinput_mode = 'pan' def init_template_widgets(self, *args): Logger.info('TemplateApp: initialising template widgets') @@ -90,9 +88,9 @@ def kb_esc(self): self.stop() else: self.display_info_toast('Double press escape to quit') - self.escape_presses += 1 - Clock.unschedule(self.reset_escape_presses) - Clock.schedule_once(self.reset_escape_presses, 0.8) + self.escape_presses += 1 + Clock.unschedule(self.reset_escape_presses) + Clock.schedule_once(self.reset_escape_presses, 0.8) def reset_escape_presses(self, *args): self.escape_presses = 0 diff --git a/terminal_widget/plugins/hide.py b/terminal_widget/plugins/hide.py new file mode 100644 index 0000000..eef93c5 --- /dev/null +++ b/terminal_widget/plugins/hide.py @@ -0,0 +1,9 @@ +from ._base import PluginBase + + +class Plugin(PluginBase): + name = 'hide' + doc = 'Hides this widget' + + def handle_input(self, term_system, term_globals, exec_locals, text): + term_system.term_widget.animate_out() diff --git a/terminal_widget/term_system.py b/terminal_widget/term_system.py index e4551ea..2e81de4 100644 --- a/terminal_widget/term_system.py +++ b/terminal_widget/term_system.py @@ -167,7 +167,9 @@ def on_every_second(self, *a): def add_text(self, text, text_time=None, level=None, add_autocomplete=True): - text = get_unicode(text) + text = str(text) + if PY2: + text = get_unicode(text) if add_autocomplete: self.add_autocomplete_words_from_text(text) if not text_time: diff --git a/terminal_widget/term_widget.py b/terminal_widget/term_widget.py index 481d944..5d1025b 100644 --- a/terminal_widget/term_widget.py +++ b/terminal_widget/term_widget.py @@ -183,7 +183,7 @@ def animate_in_small(self, focus_input=True): d = 0.05 self.ids.inputw.is_focusable = True if focus_input: - Clock.schedule_once(self.focus_input, d * 2.0) + Clock.schedule_once(self.focus_input, d * 3.0) def animate_in_big(self, focus_input=True): self.size = self.big_size @@ -200,7 +200,7 @@ def animate_in_big(self, focus_input=True): d = 0.05 self.ids.inputw.is_focusable = True if focus_input: - Clock.schedule_once(self.focus_input, d * 2.0) + Clock.schedule_once(self.focus_input, d * 3.0) def animate_out(self, *args): self.ids.inputw.focus = False diff --git a/text_editor/editor.py b/text_editor/editor.py index 4d2fb0d..f4c1505 100644 --- a/text_editor/editor.py +++ b/text_editor/editor.py @@ -11,6 +11,7 @@ Builder.load_string(''' #: import CompatTextInput kivy_soil.kb_system.compat_widgets.textinput.CompatTextInput #: import DampedScrollEffect kivy.effects.dampedscroll.DampedScrollEffect + : border_color: inputw.border_color border_width: inputw.border_width @@ -54,10 +55,22 @@ class TextEditor(BoxLayout): border_width = NumericProperty(1) focus = BooleanProperty(False) inputw = ObjectProperty() + text = StringProperty() def __init__(self, **kwargs): super(TextEditor, self).__init__(**kwargs) + def on_inputw(self, _, value): + value.bind(text=self.on_inputw_text) + + def on_inputw_text(self, _, value): + if self.text != value: + self.text = value + + def on_text(self, _, value): + if self.inputw.text != self.text: + self.inputw.text = value + def on_cursor(self, value): pass @@ -98,9 +111,12 @@ class TextEditorPopup(Popup, FocusBehaviorCanvas): def __init__(self, **kwargs): super(TextEditorPopup, self).__init__(**kwargs) - self.content = TextEditor(is_focusable=True) - self.content.bind(inputw=self.on_inputw) self.size_hint = (0.9, 0.9) + self.content = TextEditor(is_focusable=True) + if self.content.inputw: + self.on_inputw(None, self.content.inputw) + else: + self.content.bind(inputw=self.on_inputw) def on_inputw(self, _, widget): widget.bind(border_width=self.setter('border_width')) @@ -108,20 +124,21 @@ def on_inputw(self, _, widget): widget.is_subfocus = True self.subfocus_widgets = [widget] - def open(self, *args): + def open(self, focus=False, *args): super(TextEditorPopup, self).open(*args) self.content.ids.inputw.text = self.text - Clock.schedule_once(self.focus_textinput, 0) + self.content.ids.inputw.cursor = (0, 0) + if focus: + Clock.schedule_once(self.focus_textinput, 0) def focus_textinput(self, *args): winput = self.content.ids.inputw winput.focus = True - winput.cursor = (0, 0) @staticmethod - def quick_open(text): + def quick_open(text, focus=False): '''Static method that creates a new popup with argument text and opens it, then returns the new widget''' new = TextEditorPopup(text=text) - new.open() + new.open(focus=focus) return new diff --git a/various_widgets/__init__.py b/various_widgets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/various_widgets/popup_label.py b/various_widgets/popup_label.py new file mode 100644 index 0000000..d21f790 --- /dev/null +++ b/various_widgets/popup_label.py @@ -0,0 +1,33 @@ +from kivy_soil.kb_system.compat_widgets.scrollview import AppScrollView +from kivy_soil.kb_system.compat_widgets.popup import AppPopup +from kivy.properties import StringProperty +from kivy.uix.label import Label + + +class PopupLabel(AppPopup): + text = StringProperty() + + def __init__(self, **kwargs): + super(PopupLabel, self).__init__(**kwargs) + scroller = AppScrollView() + lbl = Label( + size_hint_y=None, text_size=(scroller.width, None), text=self.text) + scroller.bind(size=self._update_text_size) + lbl.bind(texture_size=self._update_text_widget_size) + scroller.add_widget(lbl) + self.content, self.lbl = scroller, lbl + + def on_key_down(self, key, modifier): + self.content.on_key_down(key, modifier) + + def _update_text_size(self, _, value): + self.lbl.text_size = (value[0], None) + + def _update_text_widget_size(self, _, value): + self.lbl.size = value + + @staticmethod + def quick_open(text, title): + new = PopupLabel(text=text, title=title) + new.open() + return new diff --git a/version b/version index c8cfe39..7371a1f 100644 --- a/version +++ b/version @@ -1 +1 @@ -3.10 +4.00