From b546e74b4368ac697196985da8cfa4e814ed10e0 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Tue, 15 Feb 2022 20:40:45 -0800 Subject: [PATCH 1/9] SplitStack control --- examples/chat.py | 4 +- examples/split.py | 59 ++++++++++++++++++++ pglet/__init__.py | 1 + pglet/splitstack.py | 114 +++++++++++++++++++++++++++++++++++++++ tests/test_splitstack.py | 49 +++++++++++++++++ 5 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 examples/split.py create mode 100644 pglet/splitstack.py create mode 100644 tests/test_splitstack.py diff --git a/examples/chat.py b/examples/chat.py index b369dce..b48e4c0 100644 --- a/examples/chat.py +++ b/examples/chat.py @@ -31,7 +31,9 @@ def main(page): vertical_align="end", controls=[messages], ) - message = Textbox(width="100%") + message = Textbox( + width="100%", multiline=True, rows=1, auto_adjust_height=True, shift_enter=True + ) def on_message(user, message): if user: diff --git a/examples/split.py b/examples/split.py new file mode 100644 index 0000000..9296528 --- /dev/null +++ b/examples/split.py @@ -0,0 +1,59 @@ +import logging + +import pglet +from pglet import SplitStack, Stack, Text +from pglet.button import Button + +logging.basicConfig(level=logging.DEBUG) + +page = pglet.page("split1") +page.title = "Split test" +page.horizontal_align = "stretch" +page.vertical_fill = True +st = SplitStack( + height="100%", + horizontal=True, + # gutter_color="#eee", + gutter_size=10, + controls=[ + Stack(width="200", min_width="200", height="100%", controls=[Text("Column A")]), + Stack(height="100%", controls=[Text("Column B")]), + Stack( + height="100%", + width="30%", + controls=[ + SplitStack( + height="100%", + gutter_color="yellow", + gutter_hover_color="orange", + gutter_drag_color="blue", + controls=[ + Stack( + width="100%", + bgcolor="lightGreen", + controls=[Text("Row A")], + ), + Stack( + width="100%", + height="200", + max_height="400", + bgcolor="lightGreen", + controls=[Text("Row B")], + ), + ], + ) + ], + ), + ], +) + + +def btn_click(e): + st.height = "90%" + st.update() + + +btn = Button("Click me!", on_click=btn_click) +page.add(btn, st) + +input() diff --git a/pglet/__init__.py b/pglet/__init__.py index a1cd592..37fdf9d 100644 --- a/pglet/__init__.py +++ b/pglet/__init__.py @@ -28,6 +28,7 @@ from pglet.slider import Slider from pglet.spinbutton import SpinButton from pglet.spinner import Spinner +from pglet.splitstack import SplitStack from pglet.stack import Stack from pglet.tabs import Tab, Tabs from pglet.text import Text diff --git a/pglet/splitstack.py b/pglet/splitstack.py new file mode 100644 index 0000000..6ab1603 --- /dev/null +++ b/pglet/splitstack.py @@ -0,0 +1,114 @@ +from typing import Optional + +from beartype import beartype + +from pglet.control import Control + +try: + from typing import Literal +except: + from typing_extensions import Literal + + +class SplitStack(Control): + def __init__( + self, + controls=None, + id=None, + horizontal=None, + gutter_size=None, + gutter_color=None, + gutter_hover_color=None, + gutter_drag_color=None, + width=None, + height=None, + visible=None, + disabled=None, + data=None, + ): + Control.__init__( + self, + id=id, + width=width, + height=height, + visible=visible, + disabled=disabled, + data=data, + ) + + self.horizontal = horizontal + self.gutter_size = gutter_size + self.gutter_color = gutter_color + self.gutter_hover_color = gutter_hover_color + self.gutter_drag_color = gutter_drag_color + + self.__controls = [] + if controls != None: + for control in controls: + self.__controls.append(control) + + def _get_control_name(self): + return "splitstack" + + def clean(self): + Control.clean(self) + self.__controls.clear() + + # controls + @property + def controls(self): + return self.__controls + + @controls.setter + def controls(self, value): + self.__controls = value + + # horizontal + @property + def horizontal(self): + return self._get_attr("horizontal") + + @horizontal.setter + @beartype + def horizontal(self, value: Optional[bool]): + self._set_attr("horizontal", value) + + # gutter_size + @property + def gutter_size(self): + return self._get_attr("guttersize") + + @gutter_size.setter + @beartype + def gutter_size(self, value: Optional[int]): + self._set_attr("guttersize", value) + + # gutter_color + @property + def gutter_color(self): + return self._get_attr("guttercolor") + + @gutter_color.setter + def gutter_color(self, value): + self._set_attr("guttercolor", value) + + # gutter_hover_color + @property + def gutter_hover_color(self): + return self._get_attr("gutterhovercolor") + + @gutter_hover_color.setter + def gutter_hover_color(self, value): + self._set_attr("gutterhovercolor", value) + + # gutter_drag_color + @property + def gutter_drag_color(self): + return self._get_attr("gutterdragcolor") + + @gutter_drag_color.setter + def gutter_drag_color(self, value): + self._set_attr("gutterdragcolor", value) + + def _get_children(self): + return self.__controls diff --git a/tests/test_splitstack.py b/tests/test_splitstack.py new file mode 100644 index 0000000..d85c94c --- /dev/null +++ b/tests/test_splitstack.py @@ -0,0 +1,49 @@ +import pglet +from pglet import SplitStack, Stack +from pglet.protocol import Command + + +def test_splitstack_add(): + s = SplitStack( + horizontal=True, + gutter_size=10, + gutter_color="yellow", + gutter_hover_color="orange", + gutter_drag_color="blue", + controls=[Stack(id="left"), Stack(id="center")], + ) + assert isinstance(s, pglet.Control) + assert isinstance(s, pglet.SplitStack) + + assert s.get_cmd_str() == [ + Command( + indent=0, + name=None, + values=["splitstack"], + attrs={ + "guttercolor": "yellow", + "gutterdragcolor": "blue", + "gutterhovercolor": "orange", + "guttersize": "10", + "horizontal": "true", + }, + lines=[], + commands=[], + ), + Command( + indent=2, + name=None, + values=["stack"], + attrs={"id": ("left", True)}, + lines=[], + commands=[], + ), + Command( + indent=2, + name=None, + values=["stack"], + attrs={"id": ("center", True)}, + lines=[], + commands=[], + ), + ], "Test failed" From 311082df7341567c6a481f71fb408f0e7ed43a36 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 16 Feb 2022 13:26:18 -0800 Subject: [PATCH 2/9] Dialog, Panel - default 'bool' props --- pglet/dialog.py | 10 ++++++---- pglet/panel.py | 14 ++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/pglet/dialog.py b/pglet/dialog.py index 225e2a0..ff335ce 100644 --- a/pglet/dialog.py +++ b/pglet/dialog.py @@ -1,12 +1,14 @@ from typing import Optional + +from beartype import beartype + +from pglet.control import Control + try: from typing import Literal except: from typing_extensions import Literal -from beartype import beartype - -from pglet.control import Control DialogType = Literal[None, "normal", "largeHeader", "close"] @@ -129,7 +131,7 @@ def type(self, value: DialogType): # auto_dismiss @property def auto_dismiss(self): - return self._get_attr("autoDismiss") + return self._get_attr("autoDismiss", data_type="bool", def_value=True) @auto_dismiss.setter @beartype diff --git a/pglet/panel.py b/pglet/panel.py index 0ec5b52..9de6710 100644 --- a/pglet/panel.py +++ b/pglet/panel.py @@ -1,12 +1,14 @@ from typing import Optional + +from beartype import beartype + +from pglet.control import Control + try: from typing import Literal except: from typing_extensions import Literal -from beartype import beartype - -from pglet.control import Control PanelType = Literal[ None, @@ -128,7 +130,7 @@ def type(self, value: PanelType): # auto_dismiss @property def auto_dismiss(self): - return self._get_attr("autoDismiss") + return self._get_attr("autoDismiss", data_type="bool", def_value=True) @auto_dismiss.setter @beartype @@ -138,7 +140,7 @@ def auto_dismiss(self, value: Optional[bool]): # light_dismiss @property def light_dismiss(self): - return self._get_attr("lightDismiss") + return self._get_attr("lightDismiss", data_type="bool", def_value=False) @light_dismiss.setter @beartype @@ -157,7 +159,7 @@ def width(self, value): # blocking @property def blocking(self): - return self._get_attr("blocking") + return self._get_attr("blocking", data_type="bool", def_value=False) @blocking.setter @beartype From 9a9f75b4939b8b1d8d78aee2f3b7ea29dd0a4bae Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 16 Feb 2022 13:26:42 -0800 Subject: [PATCH 3/9] New TextBox props: rows, shift_enter, resizable --- pglet/textbox.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/pglet/textbox.py b/pglet/textbox.py index 24f0f9e..390b12f 100644 --- a/pglet/textbox.py +++ b/pglet/textbox.py @@ -2,7 +2,7 @@ from beartype import beartype -from pglet.control import TextAlign, Control +from pglet.control import Control, TextAlign class Textbox(Control): @@ -19,10 +19,13 @@ def __init__( prefix=None, suffix=None, multiline=None, + rows=None, + shift_enter=None, password=None, required=None, read_only=None, auto_adjust_height=None, + resizable=None, underlined=None, borderless=None, focused=None, @@ -58,8 +61,11 @@ def __init__( self.prefix = prefix self.align = align self.multiline = multiline + self.rows = rows + self.shift_enter = shift_enter self.read_only = read_only self.auto_adjust_height = auto_adjust_height + self.resizable = resizable self.underlined = underlined self.borderless = borderless self.password = password @@ -173,6 +179,26 @@ def multiline(self): def multiline(self, value: Optional[bool]): self._set_attr("multiline", value) + # rows + @property + def rows(self): + return self._get_attr("rows") + + @rows.setter + @beartype + def rows(self, value: Optional[int]): + self._set_attr("rows", value) + + # shift_enter + @property + def shift_enter(self): + return self._get_attr("shiftenter") + + @shift_enter.setter + @beartype + def shift_enter(self, value: Optional[bool]): + self._set_attr("shiftenter", value) + # read_only @property def read_only(self): @@ -193,6 +219,16 @@ def auto_adjust_height(self): def auto_adjust_height(self, value: Optional[bool]): self._set_attr("autoadjustheight", value) + # resizable + @property + def resizable(self): + return self._get_attr("resizable", data_type="bool", def_value=True) + + @resizable.setter + @beartype + def resizable(self, value: Optional[bool]): + self._set_attr("resizable", value) + # underlined @property def underlined(self): From 02188bb865fce8ba1b9ba29ff9b77ebe8b643729 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 16 Feb 2022 13:26:56 -0800 Subject: [PATCH 4/9] SplitStack.on_resize event --- pglet/splitstack.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pglet/splitstack.py b/pglet/splitstack.py index 6ab1603..90a429b 100644 --- a/pglet/splitstack.py +++ b/pglet/splitstack.py @@ -20,6 +20,7 @@ def __init__( gutter_color=None, gutter_hover_color=None, gutter_drag_color=None, + on_resize=None, width=None, height=None, visible=None, @@ -41,6 +42,7 @@ def __init__( self.gutter_color = gutter_color self.gutter_hover_color = gutter_hover_color self.gutter_drag_color = gutter_drag_color + self.on_resize = on_resize self.__controls = [] if controls != None: @@ -112,3 +114,12 @@ def gutter_drag_color(self, value): def _get_children(self): return self.__controls + + # on_resize + @property + def on_resize(self): + return self._get_event_handler("resize") + + @on_resize.setter + def on_resize(self, handler): + self._add_event_handler("resize", handler) From d7e6120f232fdd784301512ae2ece8a746a391ce Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Wed, 16 Feb 2022 13:27:04 -0800 Subject: [PATCH 5/9] Examples updated --- examples/chat.py | 7 ++++++- examples/panel.py | 33 +++++++++++++++++++++++++++++++++ examples/split.py | 8 ++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 examples/panel.py diff --git a/examples/chat.py b/examples/chat.py index b48e4c0..56945a8 100644 --- a/examples/chat.py +++ b/examples/chat.py @@ -32,7 +32,12 @@ def main(page): controls=[messages], ) message = Textbox( - width="100%", multiline=True, rows=1, auto_adjust_height=True, shift_enter=True + width="100%", + multiline=True, + rows=1, + auto_adjust_height=True, + shift_enter=True, + resizable=True, ) def on_message(user, message): diff --git a/examples/panel.py b/examples/panel.py new file mode 100644 index 0000000..0e85563 --- /dev/null +++ b/examples/panel.py @@ -0,0 +1,33 @@ +import pglet +from pglet import Button, Checkbox, Panel, Text + +with pglet.page("panel-custom") as page: + + def button_clicked(e): + + p.light_dismiss = light_dismiss.value + p.auto_dismiss = auto_dismiss.value + p.blocking = blocking.value + values.value = ( + f"Panel properties are: {p.light_dismiss}, {p.auto_dismiss}, {p.blocking}." + ) + p.open = True + page.update() + + values = Text() + light_dismiss = Checkbox(label="Light dismiss", value=False) + auto_dismiss = Checkbox(label="Auto-dismiss", value=True) + blocking = Checkbox(label="Blocking", value=True) + b = Button(text="Open panel", on_click=button_clicked) + page.add(light_dismiss, auto_dismiss, blocking, b, values) + + t = Text("Content goes here") + + p = Panel( + title="Panel with dismiss options", + controls=[t], + ) + + page.add(p) + + input() diff --git a/examples/split.py b/examples/split.py index 9296528..9c1985f 100644 --- a/examples/split.py +++ b/examples/split.py @@ -6,6 +6,12 @@ logging.basicConfig(level=logging.DEBUG) + +def split_resize(e): + for c in e.control.controls: + print("size", c.width if e.control.horizontal else c.height) + + page = pglet.page("split1") page.title = "Split test" page.horizontal_align = "stretch" @@ -15,6 +21,7 @@ horizontal=True, # gutter_color="#eee", gutter_size=10, + on_resize=split_resize, controls=[ Stack(width="200", min_width="200", height="100%", controls=[Text("Column A")]), Stack(height="100%", controls=[Text("Column B")]), @@ -27,6 +34,7 @@ gutter_color="yellow", gutter_hover_color="orange", gutter_drag_color="blue", + on_resize=split_resize, controls=[ Stack( width="100%", From 49bb748ba6e26531606594bd25bda89df5695c3c Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 17 Feb 2022 09:49:02 -0800 Subject: [PATCH 6/9] Use Pglet 0.7.0 --- pglet/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pglet/constants.py b/pglet/constants.py index a372f62..e426f2f 100644 --- a/pglet/constants.py +++ b/pglet/constants.py @@ -1,4 +1,4 @@ -PGLET_SERVER_VERSION = "0.6.0" +PGLET_SERVER_VERSION = "0.7.0" PGLET_SERVER_DEFAULT_PORT = 8550 HOSTED_SERVICE_URL = "https://app.pglet.io" CONNECT_TIMEOUT_SECONDS = 30 From 909133c47260162aeb1cead85ed28d0730e07096 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 17 Feb 2022 11:08:10 -0800 Subject: [PATCH 7/9] BorderStyle allows arrays Affected controls: Image, IFrame, Stack, Text --- pglet/control.py | 24 ++++++++++++++++++------ pglet/iframe.py | 23 ++++++++++------------- pglet/image.py | 17 ++++++++++++----- pglet/stack.py | 17 ++++++++++++----- pglet/text.py | 17 ++++++++++++----- tests/test_iframe.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 108 insertions(+), 34 deletions(-) create mode 100644 tests/test_iframe.py diff --git a/pglet/control.py b/pglet/control.py index 0885b0d..bfd6764 100644 --- a/pglet/control.py +++ b/pglet/control.py @@ -1,21 +1,33 @@ import datetime as dt import threading -from typing import Optional from difflib import SequenceMatcher +from typing import List, Optional, Union + +from beartype import beartype + +from pglet.protocol import Command try: from typing import Literal except: from typing_extensions import Literal -from beartype import beartype - -from pglet.protocol import Command -BorderStyle = Literal[ - None, "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset" +BorderStyles = Literal[ + "none", + "hidden", + "dotted", + "dashed", + "solid", + "double", + "groove", + "ridge", + "inset", + "outset", ] +BorderStyle = Union[None, BorderStyles, List[BorderStyles]] + TextSize = Literal[ None, "tiny", diff --git a/pglet/iframe.py b/pglet/iframe.py index e85e91c..b851fe6 100644 --- a/pglet/iframe.py +++ b/pglet/iframe.py @@ -1,5 +1,8 @@ +from typing import List + from beartype import beartype -from pglet.control import Control, BorderStyle + +from pglet.control import BorderStyle, Control class IFrame(Control): @@ -7,7 +10,6 @@ def __init__( self, id=None, src=None, - border=None, border_style: BorderStyle = None, border_width=None, border_color=None, @@ -33,7 +35,6 @@ def __init__( ) self.src = src - self.border = border self.border_style = border_style self.border_width = border_width self.border_color = border_color @@ -52,23 +53,19 @@ def src(self): def src(self, value): self._set_attr("src", value) - # border - @property - def border(self): - return self._get_attr("border") - - @border.setter - def border(self, value): - self._set_attr("border", value) - # border_style @property def border_style(self): - return self._get_attr("borderStyle") + v = self._get_attr("borderStyle") + if v: + return [x.strip() for x in v.split(" ")] + return v @border_style.setter @beartype def border_style(self, value: BorderStyle): + if isinstance(value, List): + value = " ".join(value) self._set_attr("borderStyle", value) # border_width diff --git a/pglet/image.py b/pglet/image.py index 7d8b5e2..d29fc78 100644 --- a/pglet/image.py +++ b/pglet/image.py @@ -1,12 +1,14 @@ -from typing import Optional +from typing import List, Optional + +from beartype import beartype + +from pglet.control import BorderStyle, Control + try: from typing import Literal except: from typing_extensions import Literal -from beartype import beartype - -from pglet.control import Control, BorderStyle Fit = Literal[ None, "none", "contain", "cover", "center", "centerContain", "centerCover" @@ -108,11 +110,16 @@ def fit(self, value: Fit): # border_style @property def border_style(self): - return self._get_attr("borderStyle") + v = self._get_attr("borderStyle") + if v: + return [x.strip() for x in v.split(" ")] + return v @border_style.setter @beartype def border_style(self, value: BorderStyle): + if isinstance(value, List): + value = " ".join(value) self._set_attr("borderStyle", value) # border_width diff --git a/pglet/stack.py b/pglet/stack.py index 9ed886a..0699a13 100644 --- a/pglet/stack.py +++ b/pglet/stack.py @@ -1,12 +1,14 @@ -from typing import Optional +from typing import List, Optional + +from beartype import beartype + +from pglet.control import BorderStyle, Control + try: from typing import Literal except: from typing_extensions import Literal -from beartype import beartype - -from pglet.control import Control, BorderStyle Align = Literal[ None, @@ -213,11 +215,16 @@ def bgcolor(self, value): # border_style @property def border_style(self): - return self._get_attr("borderStyle") + v = self._get_attr("borderStyle") + if v: + return [x.strip() for x in v.split(" ")] + return v @border_style.setter @beartype def border_style(self, value: BorderStyle): + if isinstance(value, List): + value = " ".join(value) self._set_attr("borderStyle", value) # border_width diff --git a/pglet/text.py b/pglet/text.py index 4271cd9..cbb996e 100644 --- a/pglet/text.py +++ b/pglet/text.py @@ -1,12 +1,14 @@ -from typing import Optional +from typing import List, Optional + +from beartype import beartype + +from pglet.control import BorderStyle, Control, TextAlign, TextSize + try: from typing import Literal except: from typing_extensions import Literal -from beartype import beartype - -from pglet.control import Control, BorderStyle, TextSize, TextAlign VerticalAlign = Literal[None, "top", "center", "bottom"] @@ -190,11 +192,16 @@ def bgcolor(self, value): # border_style @property def border_style(self): - return self._get_attr("borderStyle") + v = self._get_attr("borderStyle") + if v: + return [x.strip() for x in v.split(" ")] + return v @border_style.setter @beartype def border_style(self, value: BorderStyle): + if isinstance(value, List): + value = " ".join(value) self._set_attr("borderStyle", value) # border_width diff --git a/tests/test_iframe.py b/tests/test_iframe.py new file mode 100644 index 0000000..c38084d --- /dev/null +++ b/tests/test_iframe.py @@ -0,0 +1,44 @@ +from typing import List + +import pglet +from pglet import IFrame +from pglet.protocol import Command + + +def test_iframe_add(): + c = IFrame(src="https://google.com", border_style="solid") + assert isinstance(c, pglet.Control) + assert isinstance(c, pglet.IFrame) + + assert c.get_cmd_str() == [ + Command( + indent=0, + name=None, + values=["iframe"], + attrs={"borderstyle": "solid", "src": "https://google.com"}, + lines=[], + commands=[], + ) + ], "Test failed" + + +def test_iframe_multiple_border_styles(): + c = IFrame(src="https://google.com", border_style=["solid", "none", "groove"]) + assert isinstance(c, pglet.Control) + assert isinstance(c, pglet.IFrame) + + # check property reading + style = c.border_style + assert isinstance(style, List) + assert len(style) == 3 + + assert c.get_cmd_str() == [ + Command( + indent=0, + name=None, + values=["iframe"], + attrs={"borderstyle": "solid none groove", "src": "https://google.com"}, + lines=[], + commands=[], + ) + ], "Test failed" From 29029b52a9079e9e416c352eac0a609e59053599 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 17 Feb 2022 11:21:31 -0800 Subject: [PATCH 8/9] 0.7.0 added to Changelog --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a36da2e..edede51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Change Log - Pglet client for Python +## [0.7.0](https://pypi.org/project/pglet/0.7.0) - Feb 17, 2022 + +Works with [Pglet Server 0.7.0](https://github.com/pglet/pglet/releases/tag/v0.7.0). + +New `SplitStack` control (based on [split.js](https://split.js.org/)) which could be used as a drop-in replacement for `Stack`, but with resize gutters instead of gaps. [SplitStack control example](https://github.com/pglet/examples/blob/main/python/controls/split.py) + +New `TextBox` control properties: +* `shiftEnter` (bool) - blocks ENTER button in `multiline` TextBox, but pops up the event, so `Stack.submit` could be triggered. New line could still be entered with SHIFT+ENTER. This is to build Discord-like message box. +* `rows` (int) - sets initial size in rows of `multiline` TextBox. +* `resizable` (bool) - controls whether `multiline` TextBox is resizable by the user. Default is `true`. `autoAdjustHeight` is still respected even if `resizable` is `false`. + +`Panel` control changes: +* `blocking` (bool) is now `true` by default. + +`border_style` property in `Image`, `IFrame`, `Stack` and `Text` allows lists, for example: + +```python +stack.border_style = ["solid", "double"] # top and bottom borders are solid, left and right are double +``` + + ## [0.6.0](https://pypi.org/project/pglet/0.6.0) - Feb 13, 2022 * Works with [Pglet Server 0.6.0](https://github.com/pglet/pglet/releases/tag/v0.6.0). From 173dca52aa42b9f9b5ce91ee88ae87fd941f9893 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Thu, 17 Feb 2022 11:23:04 -0800 Subject: [PATCH 9/9] Fix changelog [skip ci] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edede51..cbda05a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Works with [Pglet Server 0.7.0](https://github.com/pglet/pglet/releases/tag/v0.7.0). -New `SplitStack` control (based on [split.js](https://split.js.org/)) which could be used as a drop-in replacement for `Stack`, but with resize gutters instead of gaps. [SplitStack control example](https://github.com/pglet/examples/blob/main/python/controls/split.py) +New `SplitStack` control (based on [split.js](https://split.js.org/)) which could be used as a drop-in replacement for `Stack`, but with resize gutters instead of gaps. Check out [SplitStack control example](https://github.com/pglet/examples/blob/main/python/controls/split.py). New `TextBox` control properties: * `shiftEnter` (bool) - blocks ENTER button in `multiline` TextBox, but pops up the event, so `Stack.submit` could be triggered. New line could still be entered with SHIFT+ENTER. This is to build Discord-like message box.