diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c4c528ee..4296bb28c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Breaking change: The Timer class now has just one method to stop it, `Timer.stop` which is non sync https://github.com/Textualize/textual/pull/1940 - Breaking change: Messages don't require a `sender` in their constructor https://github.com/Textualize/textual/pull/1940 - Many messages have grown a `control` property which returns the control they relate to. https://github.com/Textualize/textual/pull/1940 -- Dropped `time` attribute from Messages https://github.com/Textualize/textual/pull/1940 - Updated styling to make it clear DataTable grows horizontally https://github.com/Textualize/textual/pull/1946 - Changed the `Checkbox` character due to issues with Windows Terminal and Windows 10 https://github.com/Textualize/textual/issues/1934 - Changed the `RadioButton` character due to issues with Windows Terminal and Windows 10 and 11 https://github.com/Textualize/textual/issues/1934 @@ -236,6 +235,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed issue with TextLog not writing anything before layout https://github.com/Textualize/textual/issues/1498 - Fixed an exception when populating a child class of `ListView` purely from `compose` https://github.com/Textualize/textual/issues/1588 - Fixed freeze in tests https://github.com/Textualize/textual/issues/1608 +- Fixed minus not displaying as symbol https://github.com/Textualize/textual/issues/1482 ## [0.9.1] - 2022-12-30 diff --git a/docs/examples/guide/compound/byte01.py b/docs/examples/guide/compound/byte01.py new file mode 100644 index 000000000..76f6ee69c --- /dev/null +++ b/docs/examples/guide/compound/byte01.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +from textual.app import App, ComposeResult +from textual.containers import Container +from textual.widget import Widget +from textual.widgets import Input, Label, Switch + + +class BitSwitch(Widget): + """A Switch with a numeric label above it.""" + + DEFAULT_CSS = """ + BitSwitch { + layout: vertical; + width: auto; + height: auto; + } + BitSwitch > Label { + text-align: center; + width: 1fr; + } + """ + + def __init__(self, bit: int) -> None: + self.bit = bit + super().__init__() + + def compose(self) -> ComposeResult: + yield Label(str(self.bit)) + yield Switch() + + +class ByteInput(Widget): + """A compound widget with 8 switches.""" + + DEFAULT_CSS = """ + ByteInput { + width: auto; + height: auto; + border: blank; + layout: horizontal; + } + ByteInput:focus-within { + border: heavy $secondary; + } + """ + + def compose(self) -> ComposeResult: + for bit in reversed(range(8)): + yield BitSwitch(bit) + + +class ByteEditor(Widget): + DEFAULT_CSS = """ + ByteEditor > Container { + height: 1fr; + align: center middle; + } + ByteEditor > Container.top { + background: $boost; + } + ByteEditor Input { + width: 16; + } + """ + + def compose(self) -> ComposeResult: + with Container(classes="top"): + yield Input(placeholder="byte") + with Container(): + yield ByteInput() + + +class ByteInputApp(App): + def compose(self) -> ComposeResult: + yield ByteEditor() + + +if __name__ == "__main__": + app = ByteInputApp() + app.run() diff --git a/docs/examples/guide/compound/byte02.py b/docs/examples/guide/compound/byte02.py new file mode 100644 index 000000000..c9732b499 --- /dev/null +++ b/docs/examples/guide/compound/byte02.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +from textual.app import App, ComposeResult +from textual.containers import Container +from textual.messages import Message +from textual.reactive import reactive +from textual.widget import Widget +from textual.widgets import Input, Label, Switch + + +class BitSwitch(Widget): + """A Switch with a numeric label above it.""" + + DEFAULT_CSS = """ + BitSwitch { + layout: vertical; + width: auto; + height: auto; + } + BitSwitch > Label { + text-align: center; + width: 1fr; + } + """ + + class BitChanged(Message): + """Sent when the 'bit' changes.""" + + def __init__(self, bit: int, value: bool) -> None: + super().__init__() + self.bit = bit + self.value = value + + value = reactive(0) # (1)! + + def __init__(self, bit: int) -> None: + self.bit = bit + super().__init__() + + def compose(self) -> ComposeResult: + yield Label(str(self.bit)) + yield Switch() + + def on_switch_changed(self, event: Switch.Changed) -> None: # (2)! + """When the switch changes, notify the parent via a message.""" + event.stop() # (3)! + self.value = event.value # (4)! + self.post_message(self.BitChanged(self.bit, event.value)) + + +class ByteInput(Widget): + """A compound widget with 8 switches.""" + + DEFAULT_CSS = """ + ByteInput { + width: auto; + height: auto; + border: blank; + layout: horizontal; + } + ByteInput:focus-within { + border: heavy $secondary; + } + """ + + def compose(self) -> ComposeResult: + for bit in reversed(range(8)): + yield BitSwitch(bit) + + +class ByteEditor(Widget): + DEFAULT_CSS = """ + ByteEditor > Container { + height: 1fr; + align: center middle; + } + ByteEditor > Container.top { + background: $boost; + } + ByteEditor Input { + width: 16; + } + """ + + def compose(self) -> ComposeResult: + with Container(classes="top"): + yield Input(placeholder="byte") + with Container(): + yield ByteInput() + + def on_bit_switch_bit_changed(self, event: BitSwitch.BitChanged) -> None: + """When a switch changes, update the value.""" + value = 0 + for switch in self.query(BitSwitch): + value |= switch.value << switch.bit + self.query_one(Input).value = str(value) + + +class ByteInputApp(App): + def compose(self) -> ComposeResult: + yield ByteEditor() + + +if __name__ == "__main__": + app = ByteInputApp() + app.run() diff --git a/docs/examples/guide/compound/byte03.py b/docs/examples/guide/compound/byte03.py new file mode 100644 index 000000000..829252a35 --- /dev/null +++ b/docs/examples/guide/compound/byte03.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +from textual.app import App, ComposeResult +from textual.containers import Container +from textual.geometry import clamp +from textual.messages import Message +from textual.reactive import reactive +from textual.widget import Widget +from textual.widgets import Input, Label, Switch + + +class BitSwitch(Widget): + """A Switch with a numeric label above it.""" + + DEFAULT_CSS = """ + BitSwitch { + layout: vertical; + width: auto; + height: auto; + } + BitSwitch > Label { + text-align: center; + width: 1fr; + } + """ + + class BitChanged(Message): + """Sent when the 'bit' changes.""" + + def __init__(self, bit: int, value: bool) -> None: + super().__init__() + self.bit = bit + self.value = value + + value = reactive(0) + + def __init__(self, bit: int) -> None: + self.bit = bit + super().__init__() + + def compose(self) -> ComposeResult: + yield Label(str(self.bit)) + yield Switch() + + def watch_value(self, value: bool) -> None: # (1)! + """When the value changes we want to set the switch accordingly.""" + self.query_one(Switch).value = value + + def on_switch_changed(self, event: Switch.Changed) -> None: + """When the switch changes, notify the parent via a message.""" + event.stop() + self.value = event.value + self.post_message(self.BitChanged(self.bit, event.value)) + + +class ByteInput(Widget): + """A compound widget with 8 switches.""" + + DEFAULT_CSS = """ + ByteInput { + width: auto; + height: auto; + border: blank; + layout: horizontal; + } + ByteInput:focus-within { + border: heavy $secondary; + } + """ + + def compose(self) -> ComposeResult: + for bit in reversed(range(8)): + yield BitSwitch(bit) + + +class ByteEditor(Widget): + DEFAULT_CSS = """ + ByteEditor > Container { + height: 1fr; + align: center middle; + } + ByteEditor > Container.top { + background: $boost; + } + ByteEditor Input { + width: 16; + } + """ + + value = reactive(0) + + def validate_value(self, value: int) -> int: # (2)! + """Ensure value is between 0 and 255.""" + return clamp(value, 0, 255) + + def compose(self) -> ComposeResult: + with Container(classes="top"): + yield Input(placeholder="byte") + with Container(): + yield ByteInput() + + def on_bit_switch_bit_changed(self, event: BitSwitch.BitChanged) -> None: + """When a switch changes, update the value.""" + value = 0 + for switch in self.query(BitSwitch): + value |= switch.value << switch.bit + self.query_one(Input).value = str(value) + + def on_input_changed(self, event: Input.Changed) -> None: # (3)! + """When the text changes, set the value of the byte.""" + try: + self.value = int(event.value or "0") + except ValueError: + pass + + def watch_value(self, value: int) -> None: # (4)! + """When self.value changes, update switches.""" + for switch in self.query(BitSwitch): + with switch.prevent(BitSwitch.BitChanged): # (5)! + switch.value = bool(value & (1 << switch.bit)) # (6)! + + +class ByteInputApp(App): + def compose(self) -> ComposeResult: + yield ByteEditor() + + +if __name__ == "__main__": + app = ByteInputApp() + app.run() diff --git a/docs/examples/guide/compound/compound01.py b/docs/examples/guide/compound/compound01.py new file mode 100644 index 000000000..66d8745ea --- /dev/null +++ b/docs/examples/guide/compound/compound01.py @@ -0,0 +1,52 @@ +from textual.app import App, ComposeResult +from textual.widget import Widget +from textual.widgets import Input, Label + + +class InputWithLabel(Widget): + """An input with a label.""" + + DEFAULT_CSS = """ + InputWithLabel { + layout: horizontal; + height: auto; + } + InputWithLabel Label { + padding: 1; + width: 12; + text-align: right; + } + InputWithLabel Input { + width: 1fr; + } + """ + + def __init__(self, input_label: str) -> None: + self.input_label = input_label + super().__init__() + + def compose(self) -> ComposeResult: # (1)! + yield Label(self.input_label) + yield Input() + + +class CompoundApp(App): + CSS = """ + Screen { + align: center middle; + } + InputWithLabel { + width: 80%; + margin: 1; + } + """ + + def compose(self) -> ComposeResult: + yield InputWithLabel("Fist Name") + yield InputWithLabel("Last Name") + yield InputWithLabel("Email") + + +if __name__ == "__main__": + app = CompoundApp() + app.run() diff --git a/docs/guide/widgets.md b/docs/guide/widgets.md index 70d7f55e8..78e222894 100644 --- a/docs/guide/widgets.md +++ b/docs/guide/widgets.md @@ -194,10 +194,6 @@ Let's modify the default width for the fizzbuzz example. By default, the table w Note that we've added `expand=True` to tell the `Table` to expand beyond the optimal width, so that it fills the 50 characters returned by `get_content_width`. -## Compound widgets - -TODO: Explanation of compound widgets - ## Line API A downside of widgets that return Rich renderables is that Textual will redraw the entire widget when its state is updated or it changes size. @@ -388,3 +384,191 @@ The following builtin widgets use the Line API. If you are building advanced wid - [DataTable](https://github.com/Textualize/textual/blob/main/src/textual/widgets/_data_table.py) - [TextLog](https://github.com/Textualize/textual/blob/main/src/textual/widgets/_text_log.py) - [Tree](https://github.com/Textualize/textual/blob/main/src/textual/widgets/_tree.py) + +## Compound widgets + +Widgets may be combined to create new widgets with additional features. +Such widgets are known as *compound widgets*. +The stopwatch in the [tutorial](./../tutorial.md) is an example of a compound widget. + +A compound widget can be used like any other widget. +The only thing that differs is that when you build a compound widget, you write a `compose()` method which yields *child* widgets, rather than implement a `render` or `render_line` method. + +The following is an example of a compound widget. + +=== "compound01.py" + + ```python title="compound01.py" hl_lines="28-30 44-47" + --8<-- "docs/examples/guide/compound/compound01.py" + ``` + + 1. The `compose` method makes this widget a *compound* widget. + +=== "Output" + + ```{.textual path="docs/examples/guide/compound/compound01.py"} + ``` + +The `InputWithLabel` class bundles an [Input](../widgets/input.md) with a [Label](../widgets/label.md) to create a new widget that displays a right-aligned label next to an input control. You can re-use this `InputWithLabel` class anywhere in a Textual app, including in other widgets. + +## Coordinating widgets + +Widgets rarely exist in isolation, and often need to communicate or exchange data with other parts of your app. +This is not difficult to do, but there is a risk that widgets can become dependant on each other, making it impossible to reuse a widget without copying a lot of dependant code. + +In this section we will show how to design and build a fully-working app, while keeping widgets reusable. + +### Designing the app + +We are going to build a *byte editor* which allows you to enter a number in both decimal and binary. You could use this a teaching aid for binary numbers. + +Here's a sketch of what the app should ultimately look like: + +!!! tip + + There are plenty of resources on the web, such as this [excellent video from Khan Academy](https://www.khanacademy.org/math/algebra-home/alg-intro-to-algebra/algebra-alternate-number-bases/v/number-systems-introduction) if you want to brush up on binary numbers. + + +
+--8<-- "docs/images/byte01.excalidraw.svg" +
+ +There are three types of built-in widget in the sketch, namely ([Input](../widgets/input.md), [Label](../widgets/label.md), and [Switch](../widgets/switch.md)). Rather than manage these as a single collection of widgets, we can arrange them in to logical groups with compound widgets. This will make our app easier to work with. + +### Identifying components + +We will divide this UI into three compound widgets: + +1. `BitSwitch` for a switch with a numeric label. +2. `ByteInput` which contains 8 `BitSwitch` widgets. +3. `ByteEditor` which contains a `ByteInput` and an [Input](../widgets/input.md) to show the decimal value. + +This is not the only way we could implement our design with compound widgets. +So why these three widgets? +As a rule of thumb, a widget should handle one piece of data, which is why we have an independent widget for a bit, a byte, and the decimal value. + +
+--8<-- "docs/images/byte02.excalidraw.svg" +
+ +In the following code we will implement the three widgets. There will be no functionality yet, but it should look like our design. + +=== "byte01.py" + + ```python title="byte01.py" hl_lines="28-30 48-50 67-71" + --8<-- "docs/examples/guide/compound/byte01.py" + ``` + +=== "Output" + + ```{.textual path="docs/examples/guide/compound/byte01.py" columns="90" line="30"} + ``` + +Note the `compose()` methods of each of the widgets. + +- The `BitSwitch` yields a [Label](../widgets/label.md) which displays the bit number, and a [Switch](../widgets/switch.md) control for that bit. The default CSS for `BitSwitch` aligns its children vertically, and sets the label's [text-align](../styles/text_align.md) to center. + +- The `ByteInput` yields 8 `BitSwitch` widgets and arranges them horizontally. It also adds a `focus-within` style in its CSS to draw an accent border when any of the switches are focused. + +- The `ByteEditor` yields a `ByteInput` and an `Input` control. The default CSS stacks the two controls on top of each other to divide the screen in to two parts. + +With these three widgets, the [DOM](CSS.md#the-dom) for our app will look like this: + +
+--8<-- "docs/images/byte_input_dom.excalidraw.svg" +
+ +Now that we have the design in place, we can implement the behavior. + + +### Data flow + +We want to ensure that our widgets are re-usable, which we can do by following the guideline of "attributes down, messages up". This means that a widget can update a child by setting its attributes or calling its methods, but widgets should only ever send [messages](./events.md) to their *parent* (or other ancestors). + +!!! info + + This pattern of only setting attributes in one direction and using messages for the opposite direction is known as *uni-directional data flow*. + +In practice, this means that to update a child widget you get a reference to it and use it like any other Python object. Here's an example of an [action](actions.md) that updates a child widget: + +```python +def action_set_true(self): + self.query_one(Switch).value = 1 +``` + +If a child needs to update a parent, it should send a message with [post_message][textual.message_pump.MessagePump.post_message]. + +Here's an example of posting message: + +```python +def on_click(self): + self.post_message(MyWidget.Change(active=True)) +``` + +Note that *attributes down and messages up* means that you can't modify widgets on the same level directly. If you want to modify a *sibling*, you will need to send a message to the parent, and the parent would make the changes. + +The following diagram illustrates this concept: + + +
+--8<-- "docs/images/attributes_messages.excalidraw.svg" +
+ +### Messages up + +Let's extend the `ByteEditor` so that clicking any of the 8 `BitSwitch` widgets updates the decimal value. To do this we will add a custom message to `BitSwitch` that we catch in the `ByteEditor`. + +=== "byte02.py" + + ```python title="byte02.py" hl_lines="5-6 26-32 34 44-48 91-96" + --8<-- "docs/examples/guide/compound/byte02.py" + ``` + + 1. This will store the value of the "bit". + 2. This is sent by the builtin `Switch` widgets, when it changes state. + 3. Stop the event, because we don't want it to go to the parent. + 4. Store the new value of the "bit". + +=== "Output" + + ```{.textual path="docs/examples/guide/compound/byte02.py" columns="90" line="30", press="tab,tab,tab,tab,enter"} + ``` + +- The `BitSwitch` widget now has an `on_switch_changed` method which will handle a [`Switch.Changed`][textual.widgets.Switch.Changed] message, sent when the user clicks a switch. We use this to store the new value of the bit, and sent a new custom message, `BitSwitch.BitChanged`. +- The `ByteEditor` widget handles the `BitSwitch.Changed` message by calculating the decimal value and setting it on the input. + +The following is a (simplified) DOM diagram to show how the new message is processed: + +
+--8<-- "docs/images/bit_switch_message.excalidraw.svg" +
+ + +### Attributes down + +We also want the switches to update if the user edits the decimal value. + +Since the switches are children of `ByteEditor` we can update them by setting their attributes directly. +This is an example of "attributes down". + +=== "byte02.py" + + ```python title="byte03.py" hl_lines="5 45-47 90 92-94 109-114 116-120" + --8<-- "docs/examples/guide/compound/byte03.py" + ``` + + 1. When the `BitSwitch`'s value changed, we want to update the builtin `Switch` to match. + 2. Ensure the value is in a the range of a byte. + 3. Handle the `Input.Changed` event when the user modified the value in the input. + 4. When the `ByteEditor` value changes, update all the switches to match. + 5. Prevent the `BitChanged` message from being sent. + 6. Because `switch` is a child, we can set its attributes directly. + + +=== "Output" + + ```{.textual path="docs/examples/guide/compound/byte03.py" columns="90" line="30", press="tab,1,0,0"} + ``` + +- When the user edits the input, the [Input](../widgets/input.md) widget sends a `Changed` event, which we handle with `on_input_changed` by setting `self.value`, which is a reactive value we added to `ByteEditor`. +- If the value has changed, Textual will call `watch_value` which sets the value of each of the eight switches. Because we are working with children of the `ByteEditor`, we can set attributes directly without going via a message. diff --git a/docs/images/attributes_messages.excalidraw.svg b/docs/images/attributes_messages.excalidraw.svg new file mode 100644 index 000000000..7a750a148 --- /dev/null +++ b/docs/images/attributes_messages.excalidraw.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1bbU/byFx1MDAxNv7eX4HYL3uljTtvZ14qXV1cdTAwMTHabmm7QFx1MDAwYrS3vVpVJjHEJYmzjlx1MDAwM6Wr/vd9xkDsxHFcYmkoUF1LpYk9XHUwMDFlXHUwMDFmz5znec45M/n70draenY+iNafrK1HX1phN26n4dn6b/78aZRcdTAwMGXjpI9LXCL/PkxGaStv2cmywfDJ48e9MD2JskE3bEXBaTxcdTAwMWOF3WE2asdJ0Ep6j+Ms6lxy/+P/boe96N+DpNfO0qB4SCNqx1mSXjwr6ka9qJ9cctH7//B9be3v/G/JujRqZWH/uFx1MDAxYuU35Jdcblx1MDAwM6W2bPr0dtLPreWGXHUwMDE5LomsXHUwMDFht4iHT/HALGrj8lx1MDAxMYyOiiv+1Hp7I1x1MDAxOVxmXlx1MDAxZf4uP71qXHUwMDFldk5cdTAwMWJCfNz5XFw89yjudvey8+7FWIStzigtWTXM0uQkelx1MDAxZrezztXQlc6P71x1MDAxYiZcdTAwMTiG4q40XHUwMDE5XHUwMDFkd/rR0I9cdTAwMDBcdTAwMWafTVx1MDAwNmErzs79OVa84MUwPFkrznzBN6VYwPFHSSdcdTAwMWOXjunxZd9cdTAwMDHOXHUwMDA2TFx1MDAxMmdCa+Gc4VOGbSZdTFx1MDAwNlxm+4WOZLulXG7TXHUwMDBlw9bJMezrt8dtsjTsXHUwMDBmXHUwMDA3YYopK9qdXb4yJ1x1MDAxYmjmpOKKlDFE41x1MDAxNp0oPu5kaGJN4Egz6yRcdO6kdoUxUT4rVlx1MDAxYiVcdTAwMWOZYvi8XHUwMDA1g6127iF/loet375cdTAwMWO2/qjbLYz2XHUwMDE3nk17VdmzJrwri75cdTAwMTRvUvKEdCdcdTAwMWWdb1Bz1Njdf7v59Vlj820nXlx1MDAxZrf79tvsbi9ubkafs99Pn37gpn9+4I6V2Op3pp5y9fwwTZOzRftcdTAwMWQ4+vzcbDe/dj+yNOp8lVx1MDAwM2lcdTAwMGZW0O+zt9m2ftE8dPZsZ1s8c3uvKVmFve+fvZJvXHUwMDA3XHUwMDFmtYuy5qe9/us/WKPXWazfy0/FhI9cdTAwMDbt8Fx1MDAwMrhcXFx1MDAxYsuEs0qRLlxcvVx1MDAxYvdPpn2hm7ROXG6sPypcdTAwMTlcXGGZXHQ/KFx1MDAxM4w1NH16TDDWOSGVYHZhgpntVktcdTAwMTHMNI5vkWBcZlx1MDAwYpSyhitNmoQuXtffr7hccpzj3Fx0xom70mCsml9cXNH1mFCo8IBLXHUwMDA2IW1cdTAwMTiXJTO+l0BW6YPFTCf9bC/+6lx1MDAwN1vIwFx1MDAxOKOc0lJcdTAwMTitmJto9Dzsxd3zibnLfVx1MDAxNYO1m4/Tr/8qj+gwglx1MDAxMb5XZSfab3TjY+/P6y3cXHUwMDEypVx1MDAxM66exZDmcYPDJMuSXtGgXHUwMDA1I0L0mW4tXCKRSVx1MDAxYVx1MDAxZsf9sLs/beNcXPTN1XjFLK+FoDRcbjOxOFx1MDAwMF80P785y87Tk8M/PvRenGyop6/l2d1cdTAwMDLQXFyHP2FUoJ0w0mjOlZN8UuDJWK//Vlx1MDAwYoDTcuFqXHUwMDAxyPLjblx1MDAwNVx1MDAxZfZJRCpOr1xmn8tcdPxr2j7/LyUv9ejAfvncO2Bb7tPxzyrwbz5svd9I3iE0fNVP4iMxXHUwMDE4ZsasSIitI0xp4di3JMSIXG51XHUwMDFkXHUwMDBiQJtcdTAwMDRBf1x1MDAxNmaB2ZN/v1lAQmeVM2BDKa21JZnwt2tGXHUwMDAxlFx1MDAxY3PBXHUwMDE4Mabp1kiglEDMUWEr4Gyal7Kz25NheKDmdyfDm5242/7BKnyNjE2r8JWJ3yHC4PNaXHUwMDExhi44y1x1MDAxMFx1MDAwMS5cZkDqf+2evmxcdTAwMWOIvZPPO/uHZ+HWwba551x1MDAwMNRcdTAwMDJcdTAwMTCDe8DhhFx1MDAxNFx1MDAxNVx1MDAxMTZwXHUwMDFmidRVO01GqlvD30pEWCHFdkqy1cFzOVx1MDAxNY7C072NdP9Mmv2/tt8kfGh3mns/QNV+wnSYXHUwMDE0XHUwMDE38tZVXHUwMDE42VUtXHIwqaWU7lx1MDAwNvW22dN/z2lA+3zXh+NETDNpJnhAM1x1MDAxNVx1MDAxOMRDllx1MDAxOKJcdTAwMTI9p9r2Y3TYOKRcdTAwMDZCKLUyoK/SXHUwMDA3XHUwMDFmvFx1MDAwZV+jY0vp8Fx1MDAwNfZngE9oUa/BllxmyF6oxVPh+UnMfS12W1x1MDAxNzhcco+2UivLidRcdTAwMDT8oGtcdTAwMTBH4sxoq1x1MDAxOLPThlx1MDAxNfDjVqvw8DuKUSpAXHUwMDFjYJhcdTAwMDLtcTzRVNFoRIDQXHUwMDE00TozTlx1MDAxMKeKXGZjvlx1MDAxMFCQpZtUu1x1MDAwYmG58lx1MDAxOHF55ttyoF26hjXMwjRrxv123D+eNOxyVWeRilGO69bIW9lggVx1MDAxMFx1MDAxY1x1MDAxY8KJWbJWXCKyLzU7XHUwMDBlXHUwMDA3efhcdTAwMTNoLlx1MDAxZIfN1nKjuKy8fdRvX2/V/FxietIqXHUwMDEwkZPge8hcdTAwMWLhqFx1MDAxYeVcdTAwMDKtlOKwyFx1MDAxMUlesahcdTAwMWJcdTAwMGWzzaTXizNcZv1uXHUwMDEy97PpIc7HcsMjv1x1MDAxM4Xt6at4o/K1aYpcdTAwMTj4XHUwMDFlJ6Ow4tNagaD8y/jzn7/NbN2QNmCOw3G5VVx1MDAwZTGk1OX7XHUwMDE1hoOUVYj3cZVLc21/tUjxR1x1MDAwNSNFd4/K/9+YLJWoXHJUyFxi6Vx1MDAwNWbxOGV+pLlcdTAwMWGqbCfeOVx1MDAxNudKupYqSVx1MDAwNcz4ckH+skpPZiyai0AppoEgXHUwMDAxPiUzZdgqM1x1MDAxNlxuYIxcdTAwMTKet5mRZlx1MDAwNlfCXHJcZlx1MDAxN4asYX6lUlbL+iSVsfpuqXLp+GZBqrxcdClcdJJcZrNmpDbMKWZLsLpkJcFcdTAwMDLIoCGlreRcdTAwMDaR6XJMOT/GKVx1MDAxOcWCXHUwMDFj4dxIJpnmXG6onknfXHUwMDFhyavQxLWDUfSgybLetf1RcepcdTAwMWKSW1x1MDAxZbzO4DZuarnNK1x1MDAxMIaWXHUwMDE3VYHruK3RODxcdTAwMTl0e413nz703n462uc7NnyzXHUwMDFjt01cdTAwMTc9bi9cZoRPXHUwMDA3ym92IORcYkK5qWJcZiSHXHSGLFxmLlxi4qintpZcdTAwMTOhXGKXpzZJXHUwMDAxQWl8XHUwMDFjKiGYppSAjalccnpqXHUwMDEwqXCutJLWQVx1MDAwMitxoECw6inmzrhccjKAaEtcdTAwMTdcdTAwMTO4PLdNY7HmyopRPnFtxfFQ7Vx1MDAxY/ujOrsrXHUwMDAyuWD1q57EuGWalVxue9eh/JXZ33p3tN//a3evXHUwMDExd3Y/7vY2yzi+pyjnXGahKHNcdTAwMWF6XCJcdTAwMWSjyVJcdTAwMGI5XHUwMDFl+HqTXHUwMDE2mlx05Ma3XHUwMDA3cul11GJCuJRCilx1MDAxOeFcdTAwMGLoXGJcdTAwMTk4poWk1UaUmlxcVVxckUEohqt3inFokSpM+z/Gr47aXHUwMDE59kdlbm9cYvD6XHUwMDE0xcnps+NcdTAwMTRFwFuM0IunKPOXju9pNUdqXHUwMDFiKM9kklx1MDAxY1x1MDAxM0pNXHUwMDE2c0grjDyzSmryy1x1MDAxOfVrKpE25nsyXHUwMDE0K1x1MDAwMzyFM1x1MDAwYiVcdTAwMTZOmFx1MDAxOWsqXHUwMDA2TSxcdTAwMTfqosTNbaWY44zz+UkxXHUwMDEwP2Ep5yb5XHT32039tjHpk49S4lbkXHUwMDAy0pfymN9GZ1x1MDAwNMPs28rbL5Sg3KjAhEArz4c1hEXzqk0skPA0MLVcdTAwMGaJyIxcdTAwMGJcdTAwMTJcdTAwMGYzP/HFdMF93VxuaVwiXHUwMDFj1JXvbihcdTAwMTko5vNIvKt23OrruqvFSd5dXHUwMDA1XCKrXCJKcHLtJkxcdTAwMTCHVCRcdTAwMTYve89fhbunREkqr0JcdTAwMTJcdTAwMTG3xu+PniBK5D+B4yRcdTAwMTTmXHUwMDA1woHxqGXK7y17I71Hwmu19lm8sULPqnv7QoBcdTAwMTVcdTAwMDC+c1x1MDAwNKtLw3RcdTAwMTlcZjGEyoIxW9j5XHUwMDEzXHUwMDE2c1x1MDAxNqYlsJIzoCUlXHL+Icx1pVxm44qXMMPgLtKCIVx1MDAxM1x1MDAxMXrJqvdccmo5/udcdTAwMDJKYKqM407MsIhcdTAwMDXgUWNcdTAwMTCngDyMIFMx6SExpVx1MDAxNFx1MDAwMahcdTAwMTDqIJj0xepcdGqTKlBcdTAwMDZcdTAwMTFcYsf0cKmEvK63epj4o1x1MDAwMpBVXHUwMDExJWZrzs9hpDQgS7Z4TDl/I8Q9pUptKPDcXCKl31x1MDAxZlOiwjykJFx1MDAxYvhanZXI11x1MDAxOVx1MDAxOODWQkpHgfNLQlpcdTAwMTE8h5XixTFPalx1MDAwM6+CjVx1MDAxMpE+VNNUat7cec1G9Fvc/Fx1MDAxM/LkTerLXG40o1x1MDAwNVx1MDAwNtRxSZzPiCldQIjsuEDCRuAtW2Wl1caULIBNiLBcdTAwMWPYm/zCs6pcdTAwMTbiPVU643dDglx1MDAxMrhGXvOgqbIhXHUwMDFj0mXn02XwpCPi5dtcdTAwMWKKw/MtoCfhv1baa8myUYuV/GpcdTAwMDUmN2TLur1Mtv6HPbBcdTAwMDQxp6XC7a+jyk7nTTM92n16MNJnh9REYrT//PzeUyVcdTAwMDZcdTAwMWUyZIB0+G15xfTqh4O+oCm130RcYlx1MDAwN741ruRyxi97RKVMzpGpSFx1MDAwZeYuLLm1zUxcYscs5Hupgnh5M1x1MDAxM5s4O2f3Ulx1MDAwZuaGx9Fw7dfRYPZcdTAwMWUmXrOHqVx1MDAxYlx1MDAxZE369+RcdTAwMGWmLFx1MDAxOdRtX5p4mem9SpNcdTAwMDYthTBobFx1MDAxZMSQYlvos1i8gp18jJ/2qdGmdCS2XHUwMDA2zVx1MDAxZGaH2+9cdTAwMWVcdTAwMDLCXHUwMDA09EprST66nMrbiFx1MDAwN2SsX1xi9Vx1MDAwYqeO32LeZtRCXHUwMDEw035DkrP6x0DMr1UuVY9eXG5iYZal8eEo8z7dTs7691x1MDAwMmZVoy6g9uhSLtfDwWAvw7iNg1x1MDAxNcxI3L58+aLr9dM4OmtWveKXo/zwvebw9UCJ8jjx26Nv/1x1MDAwMC2LavoifQ== + + + + Parent()Child()Child()messages (up)attributes (down) diff --git a/docs/images/bit_switch_message.excalidraw.svg b/docs/images/bit_switch_message.excalidraw.svg new file mode 100644 index 000000000..c777903e0 --- /dev/null +++ b/docs/images/bit_switch_message.excalidraw.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daXPiSNL+7l9BeL/MXHUwMDFiMdbUlXVMxMaGb7vb99l4e8MhQLZlZKBBPjfmv29cdTAwMTZ2I4FcdTAwMGVcdTAwMDTGgHteOtrYkiilqvLJfDKrsvjvQqm0XHUwMDE4Pre8xT9Li95T1Vxy/FrbfVxc/N1cdTAwMWV/8NpcdTAwMWS/2cBTrPt3p3nfrnavvFx0w1bnzz/+uHPbdS9sXHUwMDA1btVzXHUwMDFl/M69XHUwMDFidML7mt90qs27P/zQu+v8y/7cc++8f7aad7Ww7UQ3WfJqfthsv97LXHUwMDBivDuvXHUwMDExdrD1f+PfpdJ/uz9j0rW9aug2rlx1MDAwM6/7ge6pmIBcdTAwMWH44OG9ZqMrrSGEK0JcdTAwMDTtXeB31vB+oVfDs1cos1x1MDAxN52pdVx1MDAxZs9cdTAwMTfl/ZPjQ7ax/0Rf/MOL5tI2j2575Vx1MDAwN8Fx+Fx1MDAxY7x2hVu9uW/HhOqE7WbdO/dr4c3PnotcdTAwMWTvfa7TxF6IPtVu3l/fNLyO7YBI0GbLrfrhsz1GSO/oay/8WYqOPOFfXHUwMDAwwpHAiFRGXHUwMDFhQ0x05+7nJXWEUExSpoXgQOSAYKvNXHUwMDAwx1x1MDAwMlx1MDAwNftcdTAwMDdcXPFaVUSiVdxq/Vx1MDAxYeVr1HrXhG230Wm5bVx1MDAxY7House3R6Y8uvWN51/fhHhQRlx1MDAwZtXxulx1MDAxZFx1MDAwZlx1MDAxYSRKZFTvhL1Ja7vW1YH/xHumUXvrmcZ9XHUwMDEwRHLZXHUwMDEz64N6XHUwMDEz150+/Vx0vadI2NhgP1x1MDAxZl3723opuPpa11+vvJ1rslx1MDAxNJ4v9q776/f0Zl8/zF42audnXHUwMDE3x0vN3X3plVx1MDAxZla/3Kq1/rv8vL/bbjdcdTAwMWZj7b79XHUwMDE2Peh9q+a+6iSVSlx1MDAxM6FcdDdMR4NcdTAwMWP4jfpgXHUwMDFmXHUwMDA0zWo9UuOFmMBcdPz0PX9cdTAwMWM6hoos6FDNmEFRiC6MnfTuXHUwMDFjXHUwMDBiO3R62FE0XHUwMDBmO5o51ExcdTAwMDU7RiehQ80gdKhcdTAwMDZcdTAwMDZcXFGYXHUwMDE4dLK0kDJKiVx1MDAwMjKKXHUwMDE2RmPdbITH/ktXkWTf0Vxy985cdTAwMGae+4arq57YPSvPobfedVxmv/1fvFx1MDAxZjtcdTAwMWXeudtcdTAwMTTv+8xy4F9bNV6s4rN47T5cclx1MDAwZn30Nb1cdTAwMGLu/Fot7j2qKIiLbba3i1x1MDAxOP1m27/2XHUwMDFibnCSJmcu8HJcdTAwMWRcdTAwMTdHdGWhj1FcdTAwMDaGoz6qwujTle3d1fKjvOzcbLeCSiB2XaPn3nNJ7lx1MDAxMK0oVcSAVMz0oY9p6aAx1Fx1MDAxYzVeXHUwMDAyZVx1MDAwND5cZn2o60U8XHUwMDE32lx1MDAwMkEkxaGZsetqy/UtVl7+tvTilqv1tc7S4flyY1xuriu3XbF3V79cclx1MDAxZaTim+et2y/N8mXdrU+g3VBcdTAwMDSbT4dXXHUwMDBmXHUwMDE1oYOV2vPm84/bJz1cdFerNChD0PhHivVBrpZrk8lSKTIhTlx1MDAwNep3YbCnXHUwMDBm/ydwtdlg50Q6MFx1MDAxZLBcdTAwMWKRxHqKq1XGXGJ0+zGv81x1MDAwYvlaPzx+9MPqzW9qur52iJtK+NqYnKV8Z/tcbv007IEhmdijVCilmCpcdTAwMWVcIuZbzznFnqTSQTZhXHKe6uKv39Ey6ShcdTAwMTDCWkJqiFx1MDAwMJ2JPdJ9jY894qBcdTAwMDWQXHUwMDFhKbVcdTAwMDEgRpuUiFx1MDAxMYgjXHUwMDE41VQrJVx1MDAwNIYgbFx1MDAxMJqGI2Y4k6NcdTAwMDSQkV/5qTHs7chfo1x1MDAwM/ZniEbHXHUwMDAxbCd02+GK36j5jet+wd5SIUVYqX1mt4XXXHSHXHUwMDEzjFx1MDAwN1xml0pII6L+7Fx1MDAxYYHqfafb6Tiw1t1cdJAouFx1MDAwNtCJZ/dcdTAwMWG14TLlo7cnk8ZBRlx1MDAwMov/0LlyXHUwMDAxXCJdJlxmuqjSXG7sf1x1MDAxY26iXHUwMDEyQlx1MDAwNW4nXFxt3t35Ifb9QdNvhIN93O3MZVx1MDAwYv1cdTAwMWLPrVxynsWHip9cdTAwMWK0XHUwMDExLdtiP1x1MDAwYot+K0VcdTAwMTDq/tH7/T+/p1+dqdn2ldDpqLmF+PtYoVx1MDAwNDbHXHUwMDA2XHUwMDBm9yxcdTAwMWNcdTAwMTJcdTAwMGJcdTAwMWNwXHUwMDE5c7fDLFxc5UTedbbWn/ZcdTAwMGVEsFX/8m19tbV1PFtcdTAwMGKnhpJcdTAwMGJcdTAwMDKOUlx1MDAwNrhcdTAwMDTgQotcdTAwMDFcdTAwMGKHps1yXHUwMDBmSz9cYmp/pJFcdTAwMTM3cKmRXHUwMDA0RMfeTFx1MDAxOGPaUFxudNY5sCevfHZ5e7N50ZKrrfrB8u7LVc2fXHUwMDAy4Z9cdTAwMTdijujIzoFcdTAwMTmUwlx1MDAxMCOL58DSu3POoYPYYFx1MDAxMXRcdTAwMDaAw2FKwIk501x1MDAxY1ZutFx1MDAxMFIjaZtcdTAwMWEpXHUwMDFmy8ePRcp33IpcdTAwMTf89n1RfV9EsjtNVj7E4lx1MDAwZrLyfkHf4bdoXHUwMDBlM1x1MDAwNyqBXGJcdTAwMTGLm4eBr3a6t7dzclY7P0TQ3Vx1MDAxY79UV59cdTAwMGXUnINPcqTehHGMM20kovvzz1x1MDAwMmk7kUxQJJyKXGIjs4PiKfktqlx1MDAwNMbnJJ7OmI3jkpvkaUM9fttvXHUwMDFmeU+rXHUwMDE3661V9+jub5RRXHUwMDEyLCejhOacauQ8xTNK6d0559hcdTAwMDHlsGzscDot7EiThE7SdXHKXGaGXHUwMDA1ZnpzN9NzXW9Jmim7rSFcdTAwMDZ/0G1FQuZcIi4zkcQgliZcdTAwMWGAnNboy/goUVY+eZ7XPFx1MDAxMoZZVDFUY2lcdTAwMTBYnPZBjlx1MDAwMzhcdTAwMWPhyKnmiEnGP1xmcYDYNpJcdTAwMTjOXGbjUsTuXHUwMDE0+S7pUINcdTAwMDNiU+tKMMVcdTAwMDfxSFx1MDAxObOeN25cdTAwMTimm0ZcdTAwMWHLWcS6tFBcdTAwMWGpcMpcdTAwMDZcdTAwMDNSMFJcdTAwMDB6d8K0jl3xM2WzZONcdTAwMDTCKJGK2excdTAwMWJRSiZcdTAwMWW+UFx1MDAxZSmfb8aEXHUwMDAywOFlaDhcdTAwMTUnPDJH/UJcdE1cdTAwMTWnwIBcdTAwMThhM2BcdJk+U1x1MDAxYWkpU7XtK6HUUXNcdTAwMGLx99FtXHUwMDFidlxcpm0jXGJ5SiFS02G2LZ9fzatt48xBViVcdTAwMTRcdTAwMWE3XG5aRqHuq20zXHUwMDBl2jeBJyQ3mis1INhkjVx1MDAxYva3wpDbKIRaynSVQLyitlM0XHUwMDFlXHUwMDA2JY5f8zPBRJTQXHUwMDA0Ufr/1s2aeodcdTAwMTMp0WppJVx1MDAxNGozxC6JMtJcdTAwMWOQMnNjKHInKWQyXHUwMDFmXci45bOSmHFcdTAwMTNGXHUwMDAxt/8p1TLVuKFInFx1MDAxYqIpJ8SumDNJe/uZbFumYttXUqVHtG25qVx1MDAwNqpoJndD5ibRmlIovtqGtOvbX7dcdTAwMGWDXHUwMDEzctmRrdN6eHrS+jL3XHUwMDA2zlx1MDAxOIcx27WMaWl0xO67k4CGOVRqVDc8XHUwMDBm6G6z7VtFMVxyufbt7Vx1MDAxYSGS1o2ylGhcdFx1MDAxMvZcdTAwMGJVRFxuytHQvst+0eH2q/eZkfJcdTAwMGbaf1bNaq3R2Vx1MDAxNPvqWMOB6bxcdTAwMTROnMPJ9cph58vt3XVjjSw/XHR2tr4rP1f+gVwiWcxcdTAwMDJcdTAwMTRGQtTOXHUwMDAwysJ4Su/NOceTXCJcIlx1MDAxN096cnjKT92RlPCHJ/JcdTAwMGZMXCKbVFx1MDAwMqa3noWNoITRWMfyXHUwMDBm1EHDzImWTKBKkdhUTV8+oj+9sNhcdTAwMGL1ndVcdTAwMWJcdTAwMWM5r/bb94Y9++BcdTAwMDb33j9P2vfe90b6qlx1MDAxNy77WurlIVx1MDAwMu+qXHUwMDFmXHUwMDA0I6UphjiL9DRFruzjcXyusyk+XGJcdTAwMGV8hEKJfFx1MDAwYjZcdTAwMTnA1tzOjTdRxGqGPk5TXHUwMDEwXHUwMDEyQCPp6l+CJoh0XHUwMDE05UA018hR9MchXHUwMDE2hIO3oFx1MDAxOHNR5OiKcEhcdTAwMDJcdTAwMThcdTAwMTRyViosYUJahOBcdTAwMWTEM0ib1Fx1MDAwND5cbp4nTvC54eojXHR++NU/OHKXbzfPt063j2twpra93TiZjqgyM2huKTfUXHUwMDE4ZVikyz3GXHJcdTAwMGXaarxCXHUwMDAypZxRNuZcIph8OPeLpDhcdTAwMTeW7uJQXHUwMDAz4TIhXHUwMDEyd5jgikpk91xmLTP/7LmLTL1+PT2o0pNk+JzFqOLgenpAXHUwMDFlaVx1MDAwN7+wfSt7QeXg7GqLPD7Cycry/ZHoXFzuz/1yeux/ooFcYlx1MDAwNmBcZiH9XHUwMDE5XGbQxulcdTAwMDaTimMsqSE7gfH+1fSRsYrsWYKQaHTsXFxyMevF9GflzfJcdTAwMTKtrHjV1epcdTAwMGZf3vDypbgqSuXp4cF6cNsgq/7h3kZwtXfyrHaCidSBWVx1MDAxNtWN/j+aynNmMpFDQWpcdTAwMDPoXHUwMDE0WWHopHfnnHP510qUXGI6/VxcXlx1MDAxMuOg3ZpcdTAwMDJ0XG6WgVx1MDAxMYw7qITY/MtHcvlRtTCVy89/XHUwMDFk2Fx1MDAxMJv/QXVggtHMRTCAPotJw4tcdTAwMDfSO/ri6kbpx1v/oHy+fPz88Lgmzubeb0nmXHUwMDEwZsBcdTAwMTBKpCZU94FcdTAwMGa7XHUwMDAw/Vx1MDAxNmBcdTAwMTQtKFKKWFx1MDAxMDOrKjAlJGKPTbAyZDzH1VDrbnAsXq7Kl1qrs8ZdsIy+5+NcdTAwMWRXbruHpzWobZRltVx1MDAxNXYuqD5mlaO9/Vx0tNtoP/JNODva6ZgnODhZXHRX7vjW53K0gsVcdTAwMTZ/JCpRrOtcdTAwMTkpaZY+/PPuaFx1MDAxNcnDumCOnlxu1otcdTAwMTWBcTvNSmBKa3am7GdnVVx1MDAwMzbER31EXHKYkCxz+sdmgjhBT1tcdTAwMTh4+aZzToEnKXGEsrVCXGZcdTAwMTksVf25L1x0zFx1MDAwMZDKXHUwMDA2yoxzPSjX5Ga3hWNX7DFcdTAwMDWKo1x0gFx1MDAxNFx1MDAxOErqoFx1MDAxOVSaaWWUMDpJf1FAjpxcdTAwMWNmVlx1MDAwMNZFq2CxXGJ78qmvfDpaXHUwMDFhLO/SXHUwMDFjiNLG1lGp+Oqdn2ViXHUwMDE4Llx1MDAxOIGBg1x1MDAwMmCCQ+LZXHUwMDBi5b7yoVvKLO/CuybzcdpB6ElDqVx1MDAxNFx1MDAwNohmn3tyO0uv7Suh0VFjXHUwMDBi8ffxXHUwMDAyXGIhs6e2hUShsG8jRVx1MDAxZGbc7k9b6ujBe3jY2Hrcba1cdLPjXHUwMDFkzdi4XHUwMDE1qP7iaNuwbzU3nLBcdTAwMDFSgdbOXHUwMDAxQrStuELkfmB1a9FF9MxWwaNGzDqA8I8qa1crtdtccvdCfDup1Fx1MDAwM1J396dA9OeGkEM2dFBPXHUwMDEwWECL04L03pxz5FDlmGzkSDkl5Fx1MDAxNKv+UoxcdTAwMDDGSXJyXHUwMDE54/mh47Or/lx1MDAxYWLxP6z6XHUwMDBiVOZcblx1MDAxMqQo1Fx1MDAxOGGKXHUwMDA3w99u7veO/fLVKWeN3VbraYtvm4c5XHUwMDA3XHUwMDFm+mZHcIyDpTJMx6ffX91cdTAwMTaSKGq3YEPk0fhsznTcVjLvhUxcdTAwMDOMgZnvfnS6slG5fdg/uEJd9Y/LnFc3Ksd/pzySjFx1MDAxOH5yMaOmwG1qpTB00rtzzqFcdTAwMDPaMdnQkeDAVKBTrPSLMqa7ZWi/4nzNbGq/htj7Sdd+cWmyIVx1MDAwN5JQu1a1eJCVz53nNYNEmMNcdTAwMTlGuJJcdTAwMTDDgffvXHUwMDE0oFxmdaRcdTAwMTZcdTAwMTjjaoVRp1x1MDAxOZw+mmBcdTAwMDbJOCCMUZJwgy5Jpm0hhFx1MDAwMbe0Y4ZgQHljXHUwMDBiP3r7b0hQo/ixT5c+KpyqsWVd0ih0XHUwMDFjSPPRoEZ7kpSyqsN44tFcdTAwMGJlj/KJZim7qFx1MDAwYjiFXHUwMDE0ofqqw0RCps+UPVrKVGr7Sqhz1NxC/H10s2ZU5s5BjFFcdTAwMWPzUTh4PrWaV6vGucPRbFx1MDAxMSYpKvfAKm5lwDFCITaowivIx+XFke1TxolmVlx1MDAxOMVT4mFcdTAwMDFcdTAwMGW39Y1GIErBJEsmUH2UlCNFx7+uWXst51x1MDAxMnainiB2XGKw5PJLaic9bEZcXOFVaEjUmCVf+WSklF3QlVx1MDAxNEk7SGmFIYBcdTAwMDZQXHUwMDEzwz73itAspbavQXVcdTAwMWXRpuV/K1x1MDAwMIPsJW2ca6kkXHUwMDFiIUI65N5cdTAwMGVpiy1cdTAwMTnenZZbT5VvlVx1MDAxM3ExW8Mmhld7Scfmy4TQXHUwMDAwXHUwMDE4gPTvjqFcdTAwMTh3hMUqdoPdXHUwMDA0M5utuZJ6opZr1/7hSWE3i0+r9lx1MDAxMilcdTAwMGLaIEHHMEzjwtD3bun4YdVe9y7dXGb8h/37L/5hsKb3g43640rRjFx1MDAwM/9xcv+1/nxSP/56015fuffPrlduP1fGgdHsxdVUgFGCY1RQXHUwMDE4T+ndOd94wuA9XHUwMDBmT5xOXHUwMDBlT/nJurTNLpLVXpTYRJ31NCMgasYph7HLvXpLROak4muIv8hcXOEyftFXvjfkMnPdXHUwMDE59jVcdTAwMTOKj7DHobtz0Vxcalxcr9HTXHUwMDAzXb/wNm+bZ8/e3LN8Y3fO0EaBVlQgw+pDL1x1MDAxOEQvVcyWR1x1MDAxYVx1MDAxZV/sPlx1MDAxYm8ojWGESP2+vVx1MDAxYj7MXHUwMDFidiqHoVe+8E7MQV2dtFdum5Xdtb+TN2RcInu/XfRcdTAwMTNcdTAwMTKdoSjuXHLTu3POXHUwMDAxZd1hXHUwMDFloMzkXHUwMDAwNVx0d8hcYjVcdTAwMTQ0+URcdTAwMGI5f1x1MDAxOW84xGF8gDfMzHdRqTPT+Fx1MDAwNlx1MDAxOLWhd/F8V74pm9dcImgllUOUNFx1MDAxYdWKoaPpX1xiqqh0tOXz1NhccnNIzjZH7922wFx1MDAxMVozSbjmWktqTErRoOSv+11cdJTHriZMbuJmP6pcdTAwMTSwWVVBTyXplc8nS32Jc8LRJNtcdEfVXVwiK2M5l7dcdTAwMTSTdIBLpYjm3e9bgTHXgubDutS3XHUwMDE2XHUwMDE0PVx1MDAwMZPC1lxc21R+MuslXHUwMDFjprlB3VVgR1MlJ1x1MDAxOD5T1mspW7W7p1x1MDAxM1pcdTAwMWQ1uFx1MDAxMH9cdTAwMWZzp6PY5PlgSlx1MDAxZs2fVYziu1Tm19/PhJvAcFx1MDAwM8fsvrCCXCI/wchnoJZTULtcdTAwMDeE5Izh8NiKz5x9Yaex0Vx1MDAxMVxiYlBcdTAwMGJgXrn+16PDcufMO79i4erezsHDt6WD2nzsczTq3lx1MDAxM2NxfVtlm8n1XHUwMDE5t7KoXHUwMDExguf07px3PKlcXDzpXHTiaVx1MDAxMlx1MDAxYlx1MDAxZFEgqFx1MDAxZDJe8vPRO1x1MDAxZI3l/H+9nY6GeItJ73SUidlcdTAwMWNcdTAwMGaImJVSk1x1MDAxMVxuXCI6wU1l47D98uOyeaafVn/slDfXZuxcdTAwMDJcdTAwMGKUWVxu4oDgXHUwMDE4fVx1MDAxYrtiU/av61x1MDAxNkY51FZZaqT5hKpY0elkfSCPfcFNXHUwMDBmsbEvOowqqi3t0yNtVjo+Ylx1MDAwMVx1MDAxOYJ6L2JcdTAwMGKvj1t2Sq+KXuogXHTtlPq1vnSHT+Nee6kojX0/8ygoXHKbrSyI9j3lIFx1MDAxZVx1MDAwYko6XHUwMDE2JmVOkZKynnyETVx1MDAwZfjT5Y/VJ7qp1lx1MDAxZlr8eV08Xla/VudcdTAwMWWRhDukXHUwMDFidFx1MDAxM40/419waVx1MDAxYjDMfoFcdTAwMTVgPIbRpCTZlVx1MDAxNkUy0NmAjH+bXrSwJPn1VGg60EqS6UxcdTAwMWVJSsW7PWjxumen1Esxldpep9W0ul557io9hrylRFx1MDAwMup7I2yW/LBTXHUwMDFhJCZxhzpdqL7/IV5RvPBGsVx1MDAxN91W6zjEcelFXHUwMDE3OOJ+7a1zI1FcdTAwMTZcdTAwMWZ873ElReWuui/batcyWFx1MDAxMHrdUOWvhb/+XHUwMDA3KtdcdTAwMDdDIn0= + + + + ByteEditor()BitSwitch(7)Label("7") Switch() Switch.Changed( value=True)ByteEditor()BitSwitch(7)Label("7") Switch() BitSwitch.Changed( value=True)BitSwitch.Changed( value=True)Switch.Changed( value=True)A. Switch sends Switch.Changed messageB. BitSwitch responds by sending BitSwitch.Changedto its parent diff --git a/docs/images/byte01.excalidraw.svg b/docs/images/byte01.excalidraw.svg new file mode 100644 index 000000000..4d502f4f7 --- /dev/null +++ b/docs/images/byte01.excalidraw.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nOVdaU/jyFx1MDAxNv3ev1x1MDAwMjFf3pMmnlpvVY309MTSQCCsYe2nXHUwMDExMomTmCTOYidcdTAwMDRG/d/frUBcdTAwMTNncchcdTAwMDKM0400PVx1MDAxMNtJueqcU+de36r8/WVtbT16bHrrf66te72CW/OLbfdh/Xf7etdrh34jwEOs/3fY6LRcdTAwMGL9MytR1Fxm//zjj7rbrnpRs+ZcdTAwMTY8p+uHXHUwMDFkt1x1MDAxNkadot9wXG6N+lx1MDAxZn7k1cP/2n+P3Lr3n2ajXozazuBDMl7Rj1x1MDAxYe3nz/JqXt1cdTAwMGKiXHUwMDEw3/1/+Pfa2t/9f2Ota3uFyFxyyjWvf0H/UKyBko++etRcYvqNpVx1MDAwNDQ1lHP6eoZcdTAwMWZu4+dFXlx1MDAxMVx1MDAwZpewzd7giH1pXHUwMDFkzppcdTAwMTdnwVOnSW865dxWZn9jc/dm8LElv1bLR4+1frNcbu1GXHUwMDE4ZipuVKhcZs5cYqN2o+pd+cWoYlsw8vrrtWFcdTAwMDN7YnBVu9EpV1x1MDAwMi9cZoeuaTTdglx1MDAxZj3amySvLz53xJ9rg1d6+Jek2lFEU05cdTAwMTRcdTAwMDHDxetBe7XQ4GggWjNcdTAwMTCEMdAjrdpq1HAwsFW/kf7PoF13bqFaxsZcdTAwMDXFwTmiXHUwMDAwXklcdTAwMGXOeXi5VymoQzhjnFHNKSHy9YyK55crkb01RVx1MDAxZInNUJJcdTAwMTEmqVx1MDAxMmzQXHUwMDEyrz8mXHUwMDE0NKGcXHUwMDExMlx1MDAxOFT7+c1ssY+Pv+I9XHUwMDE2XHUwMDE0X3os6NRqgybbXHUwMDAzX2OYXHUwMDFhXFzTaVx1MDAxNt3o5WOUUoZQrYlcdTAwMTn0Rs1cdTAwMGaqo29Xa1x1MDAxNKpcdTAwMDO09F/9/vtcdTAwMDIwpYQn41RcdTAwMTNcclx1MDAwMsDMjtM9dpJxm72oVT047Fx1MDAxYz1cdTAwMTGze8C/Lo5T9k44xWF/XHUwMDEzqMrBW+VcZlxiUcA4XGYjVVx1MDAxYYdcdTAwMDHiR1HBXHJ2i1pcdTAwMDaqUdtccsKm20YkTIKrcLBcdTAwMTlcXHBcIimlXHUwMDEz0MqBOJpcdTAwMTIjXHUwMDA0cI5cdTAwMDRjMIpWMJJIzpn8NLCaT1x1MDAwMWucmKNY5VpQI1x1MDAwNZiZsZrb2bvZolx1MDAwZlutWq8rRHGDkqfrIFx1MDAwMasjePsnUUqIXHUwMDAxhVomULFGQKpcdTAwMWSthcbxXHUwMDAw0Fxu4CNBylx1MDAxZEJBMi1cdTAwMTVVRulxlDLtXHUwMDAwXHUwMDEwjlx1MDAxYU9cdTAwMThwS6xRlCrNSJ9Sq4dSr1bzm+FEjIJcdTAwMTKJXHUwMDE41YIowbWSM2PU69ZPN77dXHUwMDFmXFzlstmb6kaudbW3XV5cdTAwMDSj7zXjz4BRXHUwMDFjeUMkXHUwMDEwoVx1MDAxMa7EsGGQXHUwMDAygpRLg8rGjIXOUnN+yZVMsnF8Uu5YRyFccuCEzpBcdTAwMTJiXHUwMDFjoJQ5XHUwMDEyQFx1MDAxOJRQwlAshVx1MDAxYVx1MDAwNShcdTAwMTeUMWpibfxcdTAwMTlcdTAwMDCqmE5cdTAwMDIo+jBcdTAwMWMxodTM+DSsV7nNRlx1MDAwN7nuUfd8v51vcdVopFx1MDAxY59SXCJcdTAwMDJcdTAwMDFcdTAwMDEqXHRcdTAwMDe0pSP4VFx1MDAwZVCBRGVUSFx1MDAwNVIsXHTQO3ScXHUwMDFmXHUwMDA1UIZyYlx1MDAxOFx1MDAwMbWCXHUwMDEz/TSE6uRpXlKMXCJcdTAwMDSfXHUwMDE5oHv5ulxmy2euOLmqXHUwMDFmh8F5buN6p5NygGqOYVx1MDAxMZM4W0itXHUwMDE4lyNcdTAwMDBcdTAwMDVcdTAwMDdRY7hmiFWtR83HfPik7E5r+Dh8XHUwMDEynFx1MDAwNriWn1x1MDAxNzV9jlx1MDAxMdUyXHUwMDExoFx1MDAxOERcbowk+excdTAwMTBccjJnV0FwuXta7mZ36MZGePh0t5duiCqG8ZA0SmBgLVx1MDAxMYxmXGKiUjJcdTAwMDSwZIDgXHUwMDExeFAuXHUwMDA10VKpNFx0n1x1MDAxM1x1MDAwMFx1MDAxOWPKj1x1MDAxOVxccpzCcVx1MDAxMp9cdTAwMDN/P5AwwFx1MDAwMn955XsyLF+vXHUwMDE5XFxcdTAwMWTDUuT1XHUwMDA2Jjo28s2Th92jTKV0obY297fJ6WbLO9Drr+d9/33y2z5f/PW47rmt/Xz1kGarXHUwMDE33lGzenFcdTAwMTJcdTAwMGV/yo/Pd9vtxkPsfV9+S+KSxrBSsHgotSSXhu4/RiNhXHUwMDEyaaRBgVx1MDAxYVxuYd+i0eTOnEyjiluodNpe+omk3o9IU1x1MDAwMzrGx+nExuiEXHUwMDAxNjZCXHUwMDEzmc6QbTDWjSDK+099S0uGXt1x637tcWi4+uDE/lkz8e5cdTAwMGI9/LxnJFx1MDAwZZ25UfPLXHUwMDE2uus1rzSM6cgvuLXXw3W/WIzPXHUwMDFkXHUwMDA1/HBcdTAwMTffsZ2dRfNcdTAwMWJtv+xcdTAwMDdu7XzQtsUnK5qciTZCXHUwMDEzYSPSmVnmV0/Oj2vlzMP+9l6tK+tHJrpkqWeZXHUwMDExyDLGNNpcdTAwMTFcZnHYsOVHmXE4xnnKXHUwMDE4jFx1MDAwYthcdTAwMDfm9lx1MDAwNHdsXHUwMDFhXHUwMDFjuKVcdTAwMTBIOSm7x5hcdTAwMDPKSGyyTeRQYsbzJngpXHUwMDFlg3nC0oUmtXR4LWqSXHUwMDEzKqibIONcdTAwMDH6W/DduoON6FwiMsXb++vTXFzwcHK2XHUwMDE1h9o/9Vx1MDAxY+VtXGJrKlx1MDAxZI6ux3CDs7PUZlx1MDAwNMLCMTgmXHUwMDE4XHUwMDFhUUYwMljKcVx0UiBSTZokXHUwMDFj++Rcbj9cdTAwMDQ7XWpKJ6CXOlJcdTAwMTJF7dRtjb9cdTAwMWVcdTAwMDUvN4JK8vGGLFx1MDAxZNhlsXFcdTAwMWRPWHOFgVx1MDAxM5394cp+b/eCXHUwMDA2N1t1cXpPj8J6JnNZ99OuvUZcdTAwMDFcblx1MDAxYdpw9DFCXHUwMDFiMVx1MDAwZVxcITHg11x1MDAwMiNdJZeKZj9Fe5F7XHUwMDEy47t/XHUwMDE2v/Fu/1D8isRAVzFcdTAwMTQhXHUwMDE5XHUwMDFmz7fgXHUwMDFifVx1MDAxNU2/c1jyXHUwMDAytl1RXHUwMDE5kTO79HZcdTAwMTW0XHUwMDE3J1x1MDAxOFx1MDAwN4HKudFcdTAwMTKNOGUjXHUwMDEwXHUwMDA2h0hcdTAwMDGaXHUwMDE5gtJrlkHwh0qvfZzLmFwiep5s4TtGw88oyOe/XHUwMDFkdO/Pe3dunlSOXHUwMDAyUz2JVP3dwlb0d7FHt1x1MDAxZkpccpmYRqdAUTw4mUPaN/d6XHUwMDA10T5Sgp1vfDvdr9Jet1dLu7RjbzuUY8dcdTAwMTNcdTAwMDFExaPCV1ttn1NcdTAwMTP0JahasFxmMT5B2pmy8bdcdTAwMTK/iLRzQVx1MDAxM/HLJEhOyVx1MDAxY88p71xcssd446RVydz1mt6J37qst1ZB2y2G0XegfFNcdTAwMTR3zSeEhuhaKMNAg/LlQsPfNGjPTEi2v4e4o61cdTAwMDai8YRfXHUwMDA0vclVS1x1MDAxOP9cdTAwMTjCXHUwMDE5mT1cdTAwMDHf2rlcdTAwMTf+dnBcdTAwMWJcXJU293dP+d7N8U1SIUhqxFx1MDAxNyiaXHUwMDEyXGb4lFbow1x1MDAwMMRcYnCFI21lndBcblx1MDAwNZGnPadhn2VRXHUwMDFlL877qeGLTE9cdTAwMTRfyYnSqFx1MDAwNLPj9+yqVbx42pG128JZVVx1MDAxNKNm7f6gsFxu4lx1MDAwYlQ7XHUwMDE0XHJcdTAwMWJojFx1MDAwZlxy3vN4bKhcdTAwMTDZwI1SiObUii9lXHUwMDFh8Dao+UXgy03yXHUwMDAzepuZxLBRzVx1MDAxZVx1MDAxNz5cXFb35bedyq6f9erB8VngPVx1MDAxY6Q+rYF65lx1MDAxMCptvVxmXGLsj9G0XHUwMDA2c9D14pQsXHUwMDE1pfFAIJ3ySzlGK4LJX1x1MDAwNcBCJeY1NKP2QVx1MDAwMZ1dfm9Ubkfz4lG1ZIr5Mjts1VtVs1x1MDAxMvKr7MNHNPpoXCK0UWN5XHLEsCRGM2LQbi1ZaPKh8quFXCJGxiOan1x1MDAxYr06Oatsi4Ml9sbsqYf7w1x1MDAxM9K4PLy99MONh43QXFxH+fOktFxcauRXceUwjNgwNiNcdTAwMDTFS41AlztSoCnGIaGGkbRnle1iIFxmt3+Z3IOEZP+A3Vx1MDAwNMJQmD33kMtcdTAwMTauvcr1TeusXrjNPmRcdTAwMGav+fHxKuivXHUwMDA1sTRacS2YfVx1MDAxNqTHQUxt9lx1MDAwMf1cdTAwMTSVii9cdTAwMDPij9VfyVx1MDAxOcM2/ir6K6eU+INcdTAwMTbGMJgjdVbee3wqXHUwMDExt37VaoS3hzdcdTAwMWI8W8Omp1xcf7VE4bOVSURb68BGrUNfXHUwMDE2gSphUcGXeibyXHT6y4kkiH4+T5nqXG7jXHUwMDE3ZGL2gffLis1cdTAwMWNld1ftzVMhzEGvfXfwKFx1MDAxYrVcdTAwMWJcdTAwMWFcdTAwMTb2V0F90Tw4VKBNsE/0uIxVwP2AMChbXHUwMDFiheBcdTAwMDK0walVX4bmXFxY+/CLgJcmi68xVGCwMrv3velcdTAwMTVOdffkXHUwMDAyrlx1MDAwZnfunlqd/cvk1Vx1MDAwManRXjSLqL3CYM8zNbR+91V7cUKWiFrN4s44ndqL5uHZ//xcIvCNXHUwMDE1q43AXHUwMDE3XHUwMDE1XHUwMDAwea7nSPxcdTAwMTa7VffxdrtcdTAwMDS3j3fu08XxiV9/zK6C9lwiT1x1MDAxZEJcdTAwMThcdTAwMDDKL1omOlx1MDAxZb4pZp8/UvtoWaU48Vx1MDAwYii9gPj+eZxvUq0+Jcm6S23yXGKHi8yM3MKW2cs8VkpcdTAwMTc7ep9lg9Pc0Vx1MDAwZZUrgVxcw1x1MDAxY3RcdTAwMGKEXG4lXHUwMDEwejCCXFxcdTAwMDKOXHUwMDEyNppcdTAwMDctUZ2TXHUwMDBi9lx1MDAxMTeuWFx1MDAxMLmxRN1cdTAwMDCqZFx1MDAxNJv9dYHA4lx1MDAwNYbLXHUwMDE2679cdTAwMDJ3Ql1P7mJcdTAwMGZuu7RLOlx1MDAwN/eiXFwq34WN2FQ6XHUwMDA0sbnrelx1MDAwNKNcdTAwMWFiOYPFllx1MDAwMsQqUN5YXG4wNC6DlVx1MDAwMGLoxFlXXHUwMDAyRI1m0jKAoVx1MDAxYlx1MDAxOK35J9NL/pN4XG7JZUmKISbto6qZaXr+UKrlNk0+XGLK7u5Fdvss4zG6XHUwMDEyNLXPXHUwMDBlKWFcdTAwMTjWac3VyMNxXCLt7MPxP1xmX5VKtkjLsHSSXHUwMDFiXHUwMDFhI6mdgDBcdTAwMTD5+PU0n09cIppcdTAwMDZcdTAwMTLRxUhERTKLXHUwMDA0XHUwMDAyRtrtNWZmUY5+bfaK1b2C2JK1m8JJt83yuVVgkVbELoanXHUwMDA2JzOiOZ1AI0BJsfGzkmy0XZ862VEmXHJX8bTxz8Mkllx1MDAwNiaxXHUwMDA1mVx1MDAwNIllhuhcdTAwMTjR/+NcdHpmJu2dbXSzl5fhrp/fyarr6Lq3ebGzXG5MwlDGYVLYXHUwMDFiRnuoKYwxSYKtz5bckPhjoHdl0qRcYmeMSZRcdTAwMTOpOPC5kqGrwiSRXHUwMDA2JolcdTAwMDWZlFxcs6BcZlx1MDAwN61cdTAwMDWfPf66XHUwMDBmz13aOFx1MDAwN5/Ip8fbsOh+O6+vRNZcdTAwMTZcZjhcdTAwMDLjL4M84dyIcVwiXHUwMDAxKENcdTAwMTkwW8r9z1x1MDAxMkmjs/uUzS8+n0gyXHJEkouau+RtXHUwMDA3XGLaXGKGnTk7k85uy3tQb3qV7Y2n3YjSXG5/MKvBJC5cdTAwMWRFtX28rFx1MDAxNWej1T9cdTAwMDRcdTAwMWPWL4RcIooyPeVcdTAwMTHexzOJc6FcdTAwMTVTn7BR3OdcdTAwMTNcdNJAJFiQSDzR22HQIKyhmWNKOi+b/MmFyLiFfWRJJLdcdTAwMWW71XBcdTAwMTWIJFx1MDAxNbo3YiTBXHUwMDE5WFx1MDAwYi1GyjhwSuKcUlx1MDAxMFx1MDAxY12VgOQyjqWIRGYhkt1yz65cbvhcdTAwMTlnJJVcdTAwMDZcIqlcdTAwMDWJZJL34Fx1MDAxNVx1MDAxYeNaXHUwMDEy31XyLVwi8XLQpHukWij1NiumSup1fytpOUuqiKQpdZRdUkw44lxcqdFcdTAwMTlJONTuzStcZmg2bUnLx6dcdTAwMWJcZlx1MDAxMcauqvlcdTAwMTmJxNNAJL4gkZLTdlx1MDAxMuyihDm2XG7ptIS/V2td3T1G3V3voFx1MDAwYse3Okqg0WLbslx1MDAxNd2w4r3zdlLaIVx1MDAwNqdcdTAwMTljjJB0JDrCOdlcdTAwMTFcdTAwMDKhi7GRYYpM2XyVgyhcdTAwMTXUdFxuTdyYTc6U9pbcSPGu+1a+XHUwMDFjWIGd0pZjZzZodqJ//Ttcclx1MDAxY/3RlKlMfe7QXHRUTa5BXHUwMDEz9lx1MDAwYlx1MDAwM0Do2esgplx1MDAwZnA6d1CU4IDk6Fx0cUJDYzi8fkhcYuZQTe3G2VxiMymSXHUwMDBi2Fx1MDAxNyYqY1x1MDAwZfpVQFx1MDAxOVDcME5iNVx1MDAxOINcdTAwMWQ+ibGb4jNb1Gp3XHUwMDAxZWPfi1x1MDAwMFx1MDAxYVBr9Fxc+/ksOlx1MDAxMVwi1WQ8mzJcdTAwMDfVwshtR5t+UPSD8vpQXHUwMDA1xsu3fGRnkPw+OVx1MDAwYlx1MDAxZNtK4lx1MDAxMG1QSXH0XHUwMDAwhUzSwY5Ltlx1MDAwYtymXHUwMDFkYUdcdKLQunGFWGaoxi9nXGaqPLyg+Hajpm9cdTAwMTVcdTAwMTdrVIZYzFx1MDAxOIyUXHUwMDE5XG68xMbJsUZRh1GluMBwXHUwMDA1zyGcwVijam5cdTAwMThtNdA0Rtj5J1xyP4hGO7nfm1x1MDAxYpbYXHUwMDE1z1x1MDAxZJNcdLyp+LFRXHUwMDA1aNp3XHUwMDFjluzBb2tcdTAwMDOW9P94/f2v3yeenVxmYvszXHUwMDBl38H7fYn/f26bkVx1MDAxOPditCdcdTAwMTiXc+z+ynVR+81vN6Ql/bPcXHUwMDE27+RPL5LyRynRLiPAkYaAfC4vieVl7PVcdTAwMWGYY5fzckSixDebkjlaVLtgtlx1MDAwNKyiknE6X1n34i4jXVx1MDAxYtss5zLyXHUwMDBmPkaE6bBcdTAwMTmvbVnMZ0Dy5pdGg/0yhtlr3adcdTAwMGZxOrmqwG4wK5GLz1x1MDAxYt5cdTAwMGZzVWpHKk5sZa4ynCfv0bMwVzGmt3swo1ZcdTAwMTiceDSZQF1cdTAwMDNcdTAwMGVcdTAwMTdcdTAwMTLDfi2pxKhg/NuXXHUwMDA04OVcdTAwMTBflPuRNmNhrs1oM6ZL/vCMjlx1MDAwZVx1MDAwMnWMK+A4hDqW9Xid0rmDc5/hKLtcbm9cdTAwMDAnw8VsxvTd2WKNsjWxXFzbXHUwMDFkXHUwMDE57JdcZlHB2ViTKDpcdTAwMTFA8ae2JsNcdTAwMTiQK20yXHUwMDEyXHUwMDExbH8yY+B9L4+RuMaXXHQjQZHZM1x1MDAxObXC3XaVX3tX8p7C42XuW/787C7dskWtdzOGXHUwMDExm1BcdTAwMTdGjqyNXHUwMDA0YVVNc1uCi1xu9/6qJSdsLz8xXHKo3u/LYaaZi4+srkVlofEt8j/aXFzk3Duvllx1MDAwZW/xoylcdTAwMGJaiymlTVx1MDAwNsNcdTAwMTJcdTAwMTLfjuvNXCLBqSOcVpIyW9XE7ZbwXHUwMDA02Ti8f1x1MDAwZqC5QFVcdTAwMTRMKrvNLUwphl+UpoY7dr9jw6xcIlxuPcFbyH5cdTAwMGLt14xxobiKnfLDW6CEXGJit8H4XHUwMDE0b7Ew1Wb0XHUwMDE207V+LZ7CwFHhxm6/ZPc+l3w8WWB3PVWKgTJcdTAwMDKHj6nFjMX0pVx1MDAxZSNuXHUwMDA3m4KjiZG60XbhsFx1MDAxZW+UcLTRXHUwMDAynaxcdTAwMTREYYipV9pbZJJcdTAwMTBsf8awm2Qtvry8/7rbbOYjRNzrcCCW/eKLQlx1MDAwZm5yvet7XHUwMDBmm5NJZnn25aU7rep4/SVL3798/z/RhT5iIn0= + + + + 901245673Input()Switch()Label() diff --git a/docs/images/byte02.excalidraw.svg b/docs/images/byte02.excalidraw.svg new file mode 100644 index 000000000..0bd554cfc --- /dev/null +++ b/docs/images/byte02.excalidraw.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nOVdaVNcdTAwMWJLsv3uX+HwfJlcdTAwMTdx6VtZlbXdiFx1MDAxN1x1MDAxMyDAYEBcYiPWXHUwMDE3XHUwMDEzjpbUWkBcdTAwMWLqXHUwMDE2ICb831+WzKDW0kJcdTAwMGLybVx1MDAwMSYw9KKurjqZeTIrK+s/nz5//lx1MDAxMvXawZe/Pn9cdFx1MDAxZYt+vVbq+Fx1MDAwZl/+cMfvg05YazXpXHUwMDE07/9cdTAwMWS2up1i/8pqXHUwMDE0tcO//vyz4Xdug6hd94uBd19cdTAwMGK7fj2MuqVayyu2XHUwMDFhf9aioFx1MDAxMf7L/cz6jeB/261GKep4g4dsXHUwMDA0pVrU6vx6VlBcdTAwMGZcdTAwMWFBM1xu6dP/j/7+/Pk//Z+x1nWCYuQ3K/Wgf0P/VKyBio9cdTAwMWXNtpr9xlx1MDAwMkMtwGqAlytq4TY9L1xuSnS6TG1cdTAwMGVcdTAwMDZn3KEvT9XLm28n33vfOrlCrprJt2VcdTAwMTFLg8eWa/X6adSr95tV7LTCcKPqR8Xq4Iow6rRug4taKaq6XHUwMDE2jFx1MDAxY3+5N2xRT1xm7uq0upVqM1xiw6F7Wm2/WIt67iXZy8FfXHUwMDFk8dfnwZFH+ktZjzPNJVx1MDAwN8tBoVQvZ93tdNBDg6BcdTAwMTQ3hiGzI83KtOo0XHUwMDFh1Kx/sP7XoGFcdTAwMDW/eFuh1jVLg2uwqIKyXHUwMDFjXFzz8PyyXHUwMDEywWOCc8HBXGJgTL5cXFFccmqVauRcdTAwMWGiwZPGcC0541x1MDAxMjRcdTAwMGXGLlxm+oNcdTAwMDKSziM1cXC3e357v9RcdTAwMDfIv+Nd1iw9d1mzW69cdTAwMGaa7E7sxEA1uKfbLvm/xlx1MDAxZZTWRiDn2sKgXHL1WvN29OPqreLtXHUwMDAwLv2jP/9YXHUwMDAwp1x1MDAwNEZMXHUwMDA0KpeaXHUwMDAzXHUwMDA3XHUwMDEwM1x1MDAwM/VwL7NxcJcrlc+yqnfUqu9njoVcXFx1MDAxY6j8jYBKw/5cdTAwMWFStWeVRNRouZExXHUwMDAw9G9X6Fx1MDAxOSOBsGxBW7RmXHUwMDE5pEZcdTAwMWS/XHUwMDE5tv1cdTAwMGVcdTAwMDFhXHUwMDEyWtFTglx1MDAwYlx1MDAxNExcdTAwMDLAXHUwMDA0sFxuxTxcdTAwMDPMXCIqISRcdTAwMTiuxsDKjdBKk5Z5Z2DVXHUwMDEyXHUwMDEysVxuKNFqasrMWL3LXHUwMDE2dq5cdTAwMThuPcHm4VZWl0s3XHUwMDE3359cdTAwMTKwOoK3v1x1MDAxMaWSXHUwMDE5bYVS1oBRYyils6TMNKCyTOtcdTAwMTWiVHhcZpQkUaGnWW3GYcqNp1x1MDAxNFx1MDAxM2hcdTAwMTTjSlx1MDAxMKTHYGolkKDhOqrUoF6vtcOJXHUwMDE4VUYkYdRcdTAwMDJaxpTUM0P01lx1MDAwNPzkoFx1MDAwMPmbrd7RiVx0ermjy5NFIPpWXHUwMDE2/3WIaushXHUwMDExXHUwMDFjXHUwMDFhc6GsiGnK/u1cdTAwMWE9lFx1MDAxNmjotbJWxPpqXHUwMDExk1/2JXGLcXiCoDZwLq1cInvOkSz3XHUwMDA0m889qVx1MDAxNFpSoYxcdTAwMGLBUY/iXHUwMDEzJFx1MDAxMVx1MDAwMuBkXHUwMDAy31x1MDAxNUC10ElcdTAwMDBcdTAwMDVcdTAwMDKv5ZbPXHUwMDBl0KenzUaNXHUwMDFkXHUwMDFlbaszWVx1MDAwNX9v92t19yzdXHUwMDAwXHUwMDA1pjwynMqANcYy4GJcdTAwMDSiwuNkPi0zilx1MDAwYkviuiREXHUwMDBiRDlXXHUwMDA1UU28lIGAd4ZQm2jmOelcdTAwMTR6aTE7QkWb1W52oqPsTvXmQF1cdTAwMWbnXHUwMDFizcJWylx1MDAxMSrQXHUwMDEzXG40k2RnLfkvOIJQwlx1MDAwNYGT/Fx1MDAxOYtkXcUo/5hcdTAwMGahwFx1MDAwYsaoVSGUXHUwMDEzOrVUxM7WXHUwMDBmoq84TixcdKRgiXxLLYWcXHUwMDE5pfXNzon6epCv6G+t81x1MDAwNopM/qmeSzdKqas9TV6xXHUwMDAy8kaI5ckhkHJpyLcnT8pcdTAwMWHpPEi1XHUwMDE0Rsvl8iSATkBkrM//a8aJf1x1MDAxMvNcItdtXHUwMDBlXHUwMDA0/lx1MDAxN1x1MDAwYlx1MDAwMzSI5yM/k4H5cs/g7lx1MDAxOJqi4HHApGNDn+/ulvf38o9cdTAwMGbly1x1MDAxM7v1vWefXHUwMDBluzdfXq77+fzbb0P9UDtjgJc2XHTvhlx1MDAwNpuTzzl7mGDyO0+Ge9UvVrudIPWAV+LtXHUwMDAwP9X5ilx1MDAxMZaBtzVcdTAwMGV74thcdTAwMTaRpzRcYjBcdTAwMTjrVjM6rT31jTtcdTAwMWI6uus3avXe0HD1sUn989nGuy9cZuh5fcVrhq7crNcqzb52XHLKw5COakW//nK6USuV4kq+SFx1MDAwZvfpXHUwMDEzO/uz6OZWp1apNf16ftC2JazKlLCxZuQsky2dnfucibNN0auD3GiI7c7DztNcdTAwMDO/a6ZdzFx1MDAwNFwiiVx1MDAxOSlsziwoXHUwMDE0w6E4XHUwMDE0xrMo+yFcdTAwMDUgeK8uXHUwMDEyh8LTTFklXGaToKScXHUwMDE0i+PcU9pKbUnyXHJcdTAwMDNmx2NxKFx1MDAxNbXXmnnEcCHrk1x1MDAwZVrEWWL4XHUwMDAzhJRcdTAwMDaQ3P6ZXHUwMDAxfHC2KcOL6Nthpn2OXHUwMDA15V/dV6r3f/+8x1xmIFbSM8BcdTAwMTVcdTAwMTB7N9rYYVuByDyJZCVcYlrWWFx1MDAwMtdS7FxiWZFJPclQeIzcXFxcdTAwMTIoJS11PUxcdTAwMDAweORBanJ6ibxcdTAwMGIjzSh+OWlcdTAwMWOpXHUwMDE5zuNhrjV8XHUwMDEzWVx1MDAwZVjqSnLI2OzwbTX09fZ2ttjL3NbuNkVcdTAwMThcXFx1MDAxZUo/7fpXcu2Ri0lcdTAwMWVcdTAwMWYpNOByXHUwMDFjuoygXHUwMDA0XHUwMDFjOCNcdTAwMDeUL1x1MDAxNcH7XHUwMDFkXG6YMcmN4UZ/XHUwMDEwXHUwMDA0y+T5PMWJunJtZvdLg+2Tk30ondejnVx1MDAxZvLxurXFi9dmXHUwMDFkXHUwMDE0sETjcSFQgVx1MDAxMJprMCMoXHUwMDA2wo22StF1xDFSq37pXGZcdTAwMTNcdTAwMTaIQ3xcdTAwMTD0qsTgtJFSxVxy6WvQLdT3KmdcdTAwMDeb96fFQGm1n+ncdjN3aVe+XHUwMDAwzCNqa1x1MDAxOTjSKKRcdTAwMWVcdTAwMGVOo7BcdTAwMWVjKDRKXHUwMDAxXHUwMDFh5OqczDdSvmQwrTBcXHxcdTAwMTD4XG6ZXHUwMDE4XHUwMDE0JFx1MDAxMWdO+9qZXHUwMDAxvHt4e3pZzp0/tJqN/ePK9SPb7lx1MDAxZa+D7nUgNlx1MDAwNFFitprQg6PK17lwmoNVTFxuS65cdTAwMWMug+J/XHUwMDE4ZVx1MDAwMjshfv1cdTAwMTbql8yHm1x1MDAwMjRcdTAwMWaE/VxuTI4+SO2yo+RcdTAwMWNJa2fnu5eH29vlXHUwMDAzfVx1MDAxNlRcIlZu4o/N9CtgRbzBcMVcdTAwMTjXqK1cdTAwMWVGLpFfXHUwMDEwXHUwMDFjmJFutmOFXHRcdTAwMTZvo35dhlx1MDAxMCNcdTAwMDb/QbQvxnyVUfKgLLWC4eza965Z7+BBuVuA4kE2f3GQffpRTprZTpf2VS7zhli+1eSxas3GMcystYJ4XHUwMDA0MSrUy8VcdTAwMWVWqX1dviZIXHUwMDBlXHUwMDFmxHNDlpzdpvv5fHaOXHUwMDE5xd5pMTRFznd2s51K5nLv5OvGblLKcGq0L+fWI8IghZtHUVxc4Fxidok5SKZcdTAwMTDRzX2DhKWYw+/gv1x1MDAwMP08zY+igE1y7EGgy5+Nx9deQzDkq6adLVx1MDAxN24u9+5cdTAwMWHb19nKg9pPQdL7bCgmssQ5aWF0UbIxXHUwMDEwI1x1MDAxZFdSo1Nvy2VvrFL/KlwiOvTNPlxi+0WTXHUwMDFj+yW/jaivkLOjt3rQyYeNw/bOWXi3u5FcdTAwMGZt8VtcdTAwMTZTr3+N9lxiXHUwMDEw5LZcdFx1MDAxN26B8ehcdTAwMDO4XHUwMDE4XGZotDruLKRU+yrSOZqA9EHUr9TJXHUwMDA0wjDUXHUwMDEwp1Kv4fe+yO5OK7WwsbeFm9cnd5FfbZXXQvtcdTAwMWFDXHUwMDA0V1x1MDAxYmktM1x1MDAxNuOZXHUwMDE4LyBcdTAwMTY0NuS/uUVYfLnMpJXyX+E+wX6U2K9MzpyHflx1MDAxN7mMlpnxe+37aifb/da5rCt21oyAIT9Ku/5Fhlx1MDAxZVfoXCKmjjnwUeZgPcFcdNZklC1PfeqDXHUwMDEyaDR5mlx1MDAxZlx1MDAwNL5KJUZcdTAwMWaASa5cdTAwMTXOXHUwMDEzfrjZle3vR+qSseuef3WV2z82lfN1UL9cdTAwMGXC5KGRLTJWS8PNuPrlVmtcIsdcdTAwMDC4ZHL9SpWvZkyTz/1RtK/iidqXM6Vccqp4mspr8O12evuHXHUwMDE13Fx1MDAxMVet4F5vZXgzb3qp176qr32ZtVx1MDAxY7hcdTAwMTAxr/1F+2olhVx1MDAxNkwxxLRcdTAwMDd/wdL7WMnYXHUwMDA3XHSfUZcl6l9LbJBjXFzdvFx1MDAwNuDHvdtKeHPh8+vHXFw1v3lzXHUwMDFmXT9l10L/9leBgjbKXG5Ffc/lOIpcdTAwMTGFW+5O/2C5OYzVTr4xg1x1MDAwMqx8P5mTSan1XHUwMDAwidDlXGbBXHUwMDE5ojnm3W7bX4PM1VnQ/V45yD49Ni/CcmEt5o0lck9cdTAwMGLnsVx1MDAxMWqJXHUwMDAxj0RcdTAwMWaAk/7lXGKSVLNEmMJcdTAwMWRcdTAwMDRcblx1MDAxZlx1MDAxN0RubLRcdTAwMDdQZaPYXHUwMDA06Vx1MDAwMtBcdTAwMTg3mqvMrid/NVx1MDAwNpHFsutjXHUwMDFm8Ep2/VDPXHKS63HowlmT66NWOymzfuhcdTAwMDVG0+jZ9Cz6JFHSyYtbXHUwMDA10VFrxFx1MDAxY9lvW6J9b3PhUfugoDG30WlcdTAwMWSZncw6SFx1MDAxMlx1MDAxYfQss8LNYVx1MDAwYsVwNInTZVx1MDAxOXEpXWcoMlx1MDAxMlOWYS8jSpNoy7gkUWPcYtx5WMq6XGJcdTAwMTKkQZBgMUFcdTAwMDKZKEnkyoJi8VmtV1NB7ptf4ewqd1x1MDAwMUenbV3M5i+KrcZaXGJcdTAwMTKxXHUwMDE0cNEnRiBlio+LkbHOPLtcXDzF/1aLhIKcXHUwMDEyXHUwMDFiX4L7flx1MDAwNImnQZD4goKkk1x1MDAxN1xuK7DkW1x1MDAwM5+d3PXC6sPj7db53fX33Yvz5tXF1d7xWqSlXGJcdTAwMDaelsI4v1x1MDAwNInLjYpcdTAwMTLJmZGMPG9cdTAwMTJcdTAwMDNjkr2SpSRpklx1MDAxYjIuSeSSXHUwMDE4l1x1MDAxZP5cdTAwMWW5XHUwMDFkpkGScEFJSk4vUORcdTAwMGVYXHUwMDA1c1QuMVdcdTAwMDf4taDOiz/OXHUwMDFh55nocKdcXM111kGQOCrPOs+USVdcdTAwMDVCglx1MDAxYZMkblx1MDAwMazi2lx1MDAxNV5YlZs0kyhx5Vx1MDAxMu90LMfs/UiSTIMkyUXJnUmSJFJ+Uiszz1wiocvDo4ctc7b740T29i5yZYjqtbVcdTAwMTAlsjVcdTAwMWVcdTAwMTlcdTAwMWNcdTAwMDZE4MhcdTAwMWRcdTAwMDJtxkTJoGJcdTAwMDY12Wlr2GrcpFx1MDAxOa1cdTAwMTJcdTAwMTlPRoZxrnDYusiSSoMsqVx1MDAwNWVcdJP5XHUwMDFk00R7mJhjyX6mlM1sh+a8cZMrXHUwMDA03ZNWbvPworJcdTAwMTayxJXHXFzNLGt5v0TTqCwxz0pE1NZVfF2VKLFcdTAwMTlFXHRcdTAwMTmq91x1MDAxObzTaVx1MDAxMCW9mChxlrz+RLmiySDZ7GZcdH50wzB6aLDj8z14ZFmf5UpJ1S9SJUpCcY9cdTAwMTO3XHUwMDAzMJqTXHUwMDBlMSPRO2Y90iuuQFx1MDAxZFx00rRVKKuPOmhUXHUwMDFhLYjfXHUwMDEzdEAyw/DbJEmkQZLEXHUwMDEyxWRscja2XHUwMDE0ynKNZnbD1Ny/vTu4lFulzrm82snrS773mH/jXHUwMDE5/ZJcdTAwMWZWg1x1MDAwNFFiXHUwMDBiiZJcdTAwMDTjITdMMlx1MDAxN1x1MDAwZZdsJOrAhMcsXG6ppNJkqKfU4Sha7nN/qihNndN3XHUwMDE1pN1cdTAwMDNcdTAwMTQ3rmrNpNJlWnmayKawQjBqT9xxe/amXHUwMDE4J1x1MDAwZahQvZ3dej4xUrLsOdRkbOPh4TJ7XFyyt5uV3Yub3o5ccl/ebFxihn6n03pIYc0ySF6PS+4zMVx1MDAwMFx1MDAwMDV7XHUwMDE0+yravc0yntssfisp2/leOdkpJk1cdTAwMDelR1x1MDAwNJRcdTAwMTSeXHUwMDA1REt9bp1cdTAwMWIzJFx1MDAwMsJcdTAwMTJt45qTOpBcdTAwMTIlT15Rs6xcdTAwMDRALD9jSt0yJVG4+mlvZ1HWXHUwMDA345Ps1exF0bZq0elDjVx1MDAxOMw//2ey5VqsOtqilivenKnC+6tjJ0hvPFx1MDAwM2tsa1x1MDAwMtMvkzBHStr0oU5pSppC6TF62X6VXHUwMDEylzI5Yr9cXD14QSBcdTAwMTP0rfiUelx1MDAxMEvbL+Zxi1x1MDAxMo0w9CBcdTAwMTNPm1x1MDAxZlxis/Ikama50UowXHUwMDFlr/k6qMVJhpjFd5RIZVHCMPI70VatWao1K1+Gsoqet4XZn8Eg9CW22O1rbk+7jG3JXHUwMDE5aMuJdsjYRVx1MDAxNb9Nl1x1MDAxOI+4LVxyN/F+RaOtn89cdTAwMGbSloJm6fVcdTAwMTZNZ2lDLVx1MDAwMueZ0oOk1Vx1MDAxY21sqeVLk8Ajb11Zolx1MDAxY9KQbVx1MDAwMDueS1X3wyjTajRqXHUwMDEx9XyuVWtGoz3c78pNJ+LVwFx1MDAxZlNcdTAwMWH0UvFzo7qg7T5xWIlcdTAwMGZ++zyQlf5cdTAwMWYvv//7j4lXbyRj2H2No3fwgZ/i/y9W2FFPKSymjVuKPUf91Ivi9cHTRifs7FfK/KZ519RVm5RcdTAwMWSeXHUwMDFlJmKkJ1x1MDAxNaGNSctcdTAwMTSPr2v5xUS0p6VcdTAwMDRgVkvNpuSkXHUwMDAwKUO/sLguI5XpKdRu6p5+XGJpJySqXHUwMDE4XCLj5NhKrV1cdTAwMDaeVLFuet69QjO0Kp4/uUqeclx1MDAxNvpPcHxRr1x1MDAwNyivRKF1nrHifM24uJ1qzSWNylx1MDAxY8vbj/Ld7t2PTDPfXHQvXHUwMDFl7cVcdTAwMTM8QEGnXlx1MDAwMiR6iEBcdTAwMDZbWaflcZiLS4PkXHUwMDAxXHUwMDEy+pnb1YdjMlx1MDAxN19WXHUwMDAyJm4sNKGGsHSRUivecCZ8fUC+JFx1MDAxOe9FwX6z3Y1SQsZjzVmMjHNIXFzexFx00W5XojnSWKZcdTAwMGZ1Ssk4MOOJX8XXmDDkTVx1MDAwZlenkFp5XHUwMDE2hVx1MDAwMSO1ZdM2XHRbVnyJqlCPc86MsYbHp/hfhNlcdTAwMTJ5c3tHWGO4dcHkMdnWxEJcdTAwMTU19H1Q8en2IEZ8N4j5up3C0FxyoUFr9ThcdTAwMTfXnuHSXG43TSU1qlx1MDAxNyY4J1x1MDAxOZ/O0obdXHUwMDAz0vi/Qn9cdTAwMTJVrNrugIuvNfVOXHUwMDA0rPvaXHUwMDE4w+qbXHUwMDEyb56Y6kBcdTAwMWTNNeBcdTAwMWMxhLMrv36+fdPbPtBfe49cdTAwMGa7ufvd3lsvKp7GOlx1MDAxNtySS3jo0qxcdFmCSzWyuSFnrqKv1lx1MDAwMiXTgvh3Mu2QRYustFx1MDAwNPFcdTAwMTbcI34vgLqd+t5MmGJcdTAwMTJgPCW0KzFM9IjJ8Z04ibZcdTAwMGJt4uuaVslJXG7lw9ZtXHUwMDA3mtunnfLpcclcdTAwMDS3N42t9Vwi3skxcCBcdTAwMTJKqnCeXHUwMDE4+OZjq7jTuS40N+Do/vvtzs2VLrdTL1x1MDAwMZxxr79lXHUwMDAwMuVKQlxme56ESE/2tylSbqNcIm2SXHUwMDBim/CC8UWwXHUwMDA08eaTplUnXHUwMDEwb7fRXHUwMDE1zLVpwHvB+PK8e6e/jXN6iPdLe1x1MDAxNmPekFxck95tXHUwMDFlIIycI+11+linlHiTufBcdTAwMTi3RJtcdTAwMTVze9uO7NiHzPFcdTAwMDdcdTAwMGJCMUcweHJCxNLia0hRKCPIXFxcdTAwMTJNIco2gXlL44JLxlx1MDAwNbpcdTAwMTUqsmJcdTAwMTOkW1omh2pcdTAwMDGvM/Weblx1MDAxMoapN2Ouklx1MDAxNtFu5crzgIs7j1Nd8MiddDXPXHUwMDA1uVOAOFx1MDAxZXaeiX5PJ2tD9Jsxji40LJjWNGh2kFx1MDAxZftOXGJ4MnDd1zhkk1x1MDAxOPin5yd88dvt04iA9tL/XHUwMDA04VrpWV1cdTAwMGZe88t9LXjYmryjm9vU7dNzhzrFXHUwMDEz9Fx1MDAxN3D//PTz/1x1MDAwMcOHY8gifQ== + + + + 901245673BitSwitch()ByteInput()ByteEditor() diff --git a/docs/images/byte_input_dom.excalidraw.svg b/docs/images/byte_input_dom.excalidraw.svg new file mode 100644 index 000000000..d6fe3dd90 --- /dev/null +++ b/docs/images/byte_input_dom.excalidraw.svg @@ -0,0 +1,16 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nN1daVPjSNL+3r+CYL/0Row1WXfVREy8wdnc0OZo4O1ccsLYXHUwMDAyXHUwMDBijG1kmWtj/vtmXHUwMDE5XHUwMDFhSdZh2fiiPUPTLclyuiqfzCezslL//bKwsFx1MDAxODy33cW/XHUwMDE2XHUwMDE23adqpeHV/Mrj4lx1MDAxZvb4g+t3vFZcdTAwMTNP0d6/O62uX+1dWVx1MDAwZoJ2568//7yr+Ldu0G5Uqq7z4HW6lUYn6Na8llNt3f3pXHUwMDA17l3n/+yfe5U79+92665cdTAwMTb4TvghJbfmXHUwMDA1Lf/1s9yGe+c2g1x1MDAwZd79//HfXHUwMDBiXHUwMDBi/+39XHUwMDE5kc53q0Gled1we2/onVxuXHUwMDA1VET0XHUwMDFm3Ws1e8JSXHUwMDBlVFx1MDAxOcPDXHUwMDBivM4qflxc4Nbw7Fx1MDAxNYrshmfsocUr9nK7d9N9rJtm6eB43Vx1MDAwZs5L5+vhp155jcZh8Nx4XHUwMDFkiUq13vUjMnVcdTAwMDK/dev+8GpB/dfARY6/v6/TwkFcYt/lt7rX9abbsd+fvFx1MDAxZm21K1UveLbHXHUwMDAw3o++XHUwMDBlwl9cdTAwMGLhkSd7hWFcdTAwMGVQQY0xRIDR6v1s7/1cblx1MDAxY6GZltQoKpRhfXKttFx1MDAxYThcdTAwMTMo17/EXHUwMDE1q1V5KNllpXp7jeI1a+/XXHUwMDA0fqXZaVd8nK/wuse3b0xEONB117uuXHUwMDA3eFCa8PPc3rhcdTAwMTOliFx1MDAwNk01fz9jP6W9WeupwH+iI9OsvY1Ms9tohILZXHUwMDEza/1qXHUwMDEzVZ2Y+lx1MDAwNO5TKG1ksluVdndcdTAwMWJMUN7erizz48btaaVZXny/7p8/0m/7+ubO3vdye31cdTAwMWb2dlx1MDAxZb7V+MWe/H7sPsU/5dfnV3y/9Vj0vjv+wVx1MDAwZn61U/7O+ePekeeXvNXvlWL3fftbOIDddq3yqutEKlxyXFyDUSZcdTAwMDKXhte87Vx1MDAxZttGq3pcdTAwMWLC40tE4Fx1MDAwNCxj41x1MDAxYUGkzEakXHUwMDAwbrSRQlx1MDAxNkZk+iSNhEgyNURSMI6IIDJcdTAwMDKC3lxyXGadXHUwMDE2JClNQpKKXHUwMDA0JKlihlx1MDAxMEXk2CCZoYVKXHUwMDBiqUBLoYbQwnC2W83g0Hvp2XaIXHUwMDFkXa/ceY3n2IT11Fx1MDAxM1x1MDAwN2j5OXDXev7m67+jI9lx8ZPtrYiOvWep4V1bNV6s4ndx/ZiGXHUwMDA3XHUwMDFlurD3XHUwMDBi7rxaLeqUqihIXHUwMDA17+lvXHUwMDE2cSYt37v2mpXGUZqcucB7XHUwMDA1flxu8lxiRIal31x1MDAxOVx1MDAxMqI1IyxyxSDs5du4ecWe0Fx1MDAwZYBUgilKXHUwMDE1NSBj2KNcXDqCXHUwMDE4oVx1MDAwNbUjolQ2+KD3XHUwMDFhXHUwMDFkfEY5QDgxXHUwMDA0lOT4k0SiXHUwMDEwXHUwMDBl2lx1MDAwN4WAXHUwMDEwXHUwMDFhgCnSXHUwMDBmTHtKgpHDuMrQqfxSXHUwMDE4+nbknynDtVx1MDAxM1T8YNlr1rzmdVxcsDfOV1x1MDAwNCf2O1fa1q04hlx0nFPKcUg0ZyxyxVWr2rXfo1x1MDAwNFx1MDAwZcd5XHUwMDE30nAj0c1IymTiy7vN2mChLq7u/aP9l2Zru37hi43qQ1N8f0xcbmVcdTAwMWOCdp5cdTAwMWHG0KArSXm6UIJcdTAwMDHHadRIzigorlx1MDAxMjI1Kp1gpXV351x1MDAwNTj2XHUwMDA3La9cdTAwMTn0j3FvMJcs8utupdZ/XHUwMDE2v1P0XFy/iWjbO8apUvi3hVx1MDAxMEG9f7z//T9/pF5dylJs+0qodHi3L9HfWbYtl+tTKmX/4Xf7XHUwMDA2SC6oXHUwMDA2XHUwMDE1erxB9i1/jmdi39Qg86aII1x1MDAwNdVUcEMojm+c7DNcdTAwMDKOMlx1MDAwMq1cdTAwMWLHWUDjJ/rkXHUwMDFhn3UjOiQ171x1MDAwNi1cIv4vXHUwMDBihryTcsZDXHUwMDFmO1x1MDAxYrJ/6W8/updcdTAwMWKb9eP6snfmnrfXl1fOZk32L7rPOytwt/tcdTAwMWPcd8pAXHUwMDFloHJTL81zXHUwMDEwMZI/XHUwMDE4KYigVOssqGtmJEFcdTAwMTdQnMmkz/6cI13mI11MXHLpTCWRLpNIXHUwMDA3gaKCoWND+qRjiEi4MyCGWPnF7L/+bNpcdTAwMTNV9NdcdTAwMWS38/fPxaDV/rn4s5lcdTAwMWVZXGJcdTAwMWW703vg0HCvgtHjilx1MDAwMW6rP64oIPtcdTAwMDc8slxmeVg/TFx0Rr+AlEdcdTAwMTfH6crhnqqdbHzb89ae2lxcb7i+u+TNe/6NXHUwMDEx9MlcdTAwMThYIf9AXHUwMDAyQkGyXHUwMDE4UjlnjqaMIDUhXHUwMDFjgOpMoH442jeQXHUwMDA0qkpE+5Io4JJFvtdsXFzyzfpccl8+u1/deKhcXH6/a+12N48vr4q6uJuLk12x3azed4+f6jdUXHUwMDFk3d22XHUwMDFlxuA6XHUwMDBmbsr153OvfHrbeVhT61stf3XDXHUwMDFiw323r6ube+tcdTAwMGZPasv9UTnaa+/drWP8MS6XzFx1MDAxNGOhXk3KJUtNM7HOuOGaUiiO9fTpn6lPLoB1XHS5WEd6zlwiWFdcdTAwMTPDuklL7CV8sjXBlKE846Pf8+OUbcJss9nuXHUwMDA2WXm9XGbv+9G83lx1MDAwMCeVltf7JebojpYh581cdTAwMDKfMMC1UkNk9nboXHUwMDBm/2bHXdl9WuvSMrku7Z+Wm/PuZylTXHUwMDBlXHUwMDE4xJbgXHUwMDFh/zPxxJ5cdTAwMDTiXGKNJopcdTAwMTjFpFx1MDAxMlx1MDAxM3SzJMXNykTujjD0sdJwOevQl2JQeVjdO395emr5vPHy/HJ/dTRrP3tx841cdTAwMWVtPK2xTX/l4rl+u+5cdTAwMDdwPob78v3DXHUwMDA3s7NcdTAwMTI86lx1MDAxMu8snWnXK2k1Lj+rqWZcIlT4XHT5WcZ19pI2hnpcbqO+SEZ4XHUwMDEw1tOnf879LOU8XHUwMDBm65Q6MFx1MDAxNayblKx90s1SQimyXHUwMDAxXHUwMDBl41vSXHUwMDFlp1x1MDAxNn7QzXrB4aNcdTAwMTdU619hun52gJNK+NmInFx1MDAwYlx1MDAxZlx0aVx1MDAxNWR6WoywMJzT0lx1MDAxNF/AfvK8yr1/e3dSWq+wXHUwMDE3ft899PZmvIgmXHUwMDA3pp7AXHUwMDExxiiOP4wzwXhcZn2cI8lVlFx1MDAwMVd4XHRcdTAwMDOSzXI/nHoyPIk/naS5XHUwMDA06ZEydIy5p9FcXO3a/snN7qM8XT2g66XT1WD7eUOuTSFcdTAwMWI82HVNKWurc7CjqUKFkUOkg9KHc86xI3KxI+LY6WfP41xcnyFJ6CSztkhmmcJIVY4vXHUwMDFiND9cdTAwMTHia9j1mvbslSrWW42a6//9c/FcdTAwMTKDss60XHUwMDEzt1x1MDAwM1xcQb9DKyR9LkwzXHUwMDBiRVx1MDAxNCGZMLWL2ZrpyIrdwHXUXFzDNadcZpNI5lx1MDAxMFsqXHUwMDAwXHUwMDA2tZ/RXHUwMDE4TJm2oSaA0JZeXG5cdTAwMWVZdlx1MDAxZTdOwaFcdTAwMTRxITV+XHUwMDE0YsPoXHUwMDE0jyeYXHUwMDAzQjFcdTAwMDCNYT5cdTAwMThcdTAwMTFBwHuih4PNtqthKrg+XaVIwaJcZulQw7mixtbgXHRJZFpVXHUwMDA2OFx1MDAxOEpJW4kqUG6NLMYkvnyhSpF8VL9cdTAwMGJFcJ654UJcdTAwMTBbmWKkyKhfXHUwMDAxblBcdTAwMWKkoTjjSlFJXHUwMDEyUn2qWpFs9bavpGKHN/xcdTAwMTL9PbSFo1xcZK5cdTAwMWZcdTAwMTODNIUoPVx1MDAwNIvPT4PMqYljWjvSXHUwMDE45F2ao1wi0ThcdTAwMTVBaDiK4yBQsGUz0ULtcVx1MDAxN8JRXHUwMDA3yZ8gqM5MUqHCiVx0iVx0d4iU1GhGgWiTXFy00ohcdTAwMGIzY/uGcpFIiD9++5afWY7bXHRcdTAwMDaaIZxcdTAwMTjYKmI0/ilW0NixJoxwUFx1MDAxY936aOYtP1xuj8uEQ8RcYrJeXCJ6dc9JmexKqVwi1iRTYVx1MDAxZFx1MDAxNv/cxi1Tse0rodJDmrb8JFx1MDAwNc+u9MVcdTAwMTkgXHUwMDE0lVx1MDAxNYrnXGJ3L1a+lcs3zD+puZetnSXxtNahc15cdTAwMWbDwDjoVDRDXHUwMDAzJ1x1MDAwNbKPOIMj3DGMgFx1MDAxMpzgXHUwMDBmzsLkXCKtVINcdTAwMTZJirwlXHRcdTAwMTFcdTAwMTNcdTAwMWMjv1kvXHUwMDA3bJ2s3DWvK8es/qw3ZGX95P6YXHUwMDA2s15cdTAwMWX/XHUwMDFkKtY4z2RcdTAwMWNI5jgyXHUwMDEyXHUwMDE4XCL3kT5Nc1x1MDAwZUkh8iDJtMOnXHUwMDAzSV0obY9cdTAwMDabXHUwMDAzj/Ki3yf1XHUwMDExVn1NN2k/wJNkXHUwMDE3p4285yW6kbBcdTAwMGZ2XHUwMDFj41x1MDAwZWLZbWHU5du3OeX5XHUwMDFjtFx1MDAwM5pcYs21pHh5nOczbVx1MDAxYyEw1jWogISTSEZy/LlcZoZ2TuGAI8pcdOEqJVx1MDAwNckxOqfCbv/UWuNcdTAwMTU6uVx1MDAxYk1cdTAwMGKD0IzuZfpccnNcdTAwMTn5QInzalx1MDAwMkJyYFx1MDAwMlVcdTAwMTlcdTAwMDNpmuTVysGJ14ZzrnrUO5k0KMT1XHUwMDBix1x1MDAxZuBgyI7ScMlQ+YxkJpbOeFx1MDAxNVxuldIuliE1XHUwMDA2IGj4PzfXz9Zt+0pq9ZBsP9u+oYPKsm+Ca/u/LJ6qzedZc2rfcMxcdTAwMWSltVwiXHUwMDFjObTk0X3mr1v6LOtAdkVcdTAwMDBpXHUwMDA3V5NcXFSh1JGglOQ2KWzT5CnMn6HeS6KYZFRcdTAwMTiI7C59s29cXKJcdTAwMWVcdTAwMTk6VLHApzNvhTf1UVx1MDAwMTiQhNg9myotJ1xuXHUwMDBlx1x1MDAxM1xubZvhQFxig1x1MDAxMXf0XHI0uL8kslx1MDAxYslcdTAwMDBQjVx1MDAxOLHqlCqSoIg80au8xVA8ZZPhZ7Jt2VptX1x0fVx1MDAxZdKy5Zc1yki7ikSaXHUwMDE2Z5zhNFx1MDAxNN/Rx5fXuydrm62V04p7UX95MK2no/05XHUwMDBmmohgjpBMqN6ym2AsXHUwMDFlNSlGXHUwMDFjXHUwMDA13O5lRf+OqjY541asrlEjhVRy9u076lx1MDAwZlx1MDAxYvewXq67bOX2qnbaofWDy4PfvfwwXHUwMDFhNE+q/FBFdnb2QVx1MDAxMqmcXHUwMDEyisjiXHUwMDAxVfoszTlcIqXMRaSgXHUwMDBlnVxuXCLT1oKTaVxmLa2QZjo1/kPrYDjVo6QxdiqXbuPrz0X4ufjvhakmMlx1MDAwNriS/kRGXFzQXHUwMDBmOEStMkszKJJcdTAwMWZi2DBcdTAwMWLqXHUwMDBlzrfcs+f7Yyit7m6elPeuyrf1kzmHXHUwMDFm1cg4rLZJXHRUXHRcdTAwMTJcdTAwMDdcdTAwMWa6Q0OQiFCMXHUwMDAzlJi5N1Q4I2Cj4Fx1MDAxOXvD/bVcdTAwMDefL61cdTAwMWZcdTAwMWRstq+6Tb3JO+V9PVx1MDAxZl7LkGhcdTAwMTOWSXktozKz78hsXHUwMDE1V2irXHUwMDBiwyZ9NOdcdTAwMWI2XGYsj8yCXHL6LD5cctjIlK5cdTAwMTApLsvG8kzy6dTLXHUwMDBmq4BcdTAwMWZzWW9F6FN2V1x1MDAwM1xmfb+7XG6FzEVbdlbKiEw3hbGxJU5miEYsuWx8XrNSXG5pIJpcdTAwMTZGXHUwMDE1XHUwMDExOtqAoVcnrLjDNZ5lXHUwMDAwljH3yzU+xFx06ShJXGIyM2X7f6mUjaE2O8mRI1wiU1x1MDAwNIPxNEvm3Fx1MDAxNcG36qhlmEF9jaJ6tI0tXHUwMDA1k1LD1LIwu5hiXHUwMDA3lnDbtimSXHUwMDEyXHRrWVxmgM1cXDGMXHUwMDA2XHUwMDAwVT7x5Vx1MDAwYuWl8olmXFwornBcdTAwMTJccqBMVEguaVIocIxdf+VS4JByopNCfabMVClTue0rqdbh/b5Ef49QPZhTXsOBMsX0XHUwMDEwW93z+dW82jejUd9Q4yjYoSe0b1x1MDAwZp7CIJlcdTAwMTnCJVx1MDAxOE3BTM7AcVxmXHUwMDA3mN1Qj26FoUFlKVEx5460+1x1MDAwNFx1MDAxNcFYWEqW4OnUNiuj3JjZXHUwMDE2XHUwMDEwXCJcdTAwMTOZZNa9sIGzffKQMFx1MDAxYZtXt/tyeKxz3ZstYY5cdTAwMTFcYitbISGYXHUwMDFk99FcZlxcPjWJXHQlXGJcdTAwMTFcdTAwMDS0XaCgXGIxkpRcdFx1MDAxY2XLqI0t39WA4n1q+5at2r2z/Uo9pHlcdTAwMWKQeYfsblx1MDAxZb2F5KF6hZ7cXHUwMDFmXFysLYlD+dgodVx1MDAwZk42Lk7rL4ejmbjpdVx1MDAxNEDn4dhcdTAwMGVcdTAwMTmcXHUwMDBiTanS8ZhcdFx1MDAwN8hRXGZcdTAwMDRcdTAwMDBnyI50dtA0rY5cdTAwMDJcdTAwMTKhoW2XlbGFTe9cdTAwMWE1VLKhc7ZVW/Jqa+Z4/9ErLZeO5OXZ9lx1MDAxNDrh5N634ZLzXHUwMDFk0Tl9MFtcdTAwMDcvXHUwMDE1v96+WSndjeG+3aPmcX1rp7y0XHUwMDEx1O9fSof+j63zsW3L1EyISMg8qeSINJlduoSxLbmH6VxunDr5c05muGF5SKfMoVNBetp+5pS2PcCR3JNxds1cdTAwMWOnXHUwMDBlhnP9sX5cdTAwMDJquqWJXHUwMDAzXFxUdj9cdTAwMDE1cpqEyuwulkTbsihFSPE8Sb7pnFfoMeIwRjFcdTAwMWVlxvbcVvE4QlDuYEBLMNzoXHUwMDE5oslVJ2JcdTAwMDTg2MpcdTAwMGWjgCkqIa2Fllx1MDAwMlx1MDAwNykos5V0XHUwMDFhf6KlRG9xXHUwMDA0Tlx1MDAxYTBOh+rqM/Y4QlxiSkfKaI57I1x1MDAxMjhUXCJZx+jbprq0MskwQjjEXHUwMDFlN4RcdG2f/ZBk7IWiiHz0xqNcYklt/Vx1MDAwZVx1MDAxN7ZcZl0xlVx1MDAxMEk7XHUwMDEyY1x1MDAxZmVcdTAwMWK5aUWJTM7GZ1xuXCKy9dq+XHUwMDEyXHUwMDFhPdYgQqvMVkVWJ1RsZ/Ug81Zb3VIrRy+Px7v3z3urm9v+9f7jjDtcdTAwMTVcclxcdeFUOVxmXGZD48VRoYyMW7de9Vx1MDAwZdo1MDaLRcRcdTAwMDStW7FcdTAwMTiCXG6NTidatDCbXHUwMDEwgtzfXHUwMDFjlLdWa+X25Vn1uHPaZJuXT7831Udzwya+XHUwMDBiXHTNWnZBXHUwMDFkKG2fVjBE5jJ9muZcdTAwMWOSnDg6XHUwMDA3krZ8ZyqQTOvAkkL2kVx1MDAxYtk1hOl0YFx1MDAxOVpcdTAwMGI/RvZ/lcWoqdfvXGZwJln1O+qj9TsmO9TWUvPeWlFh+Fx1MDAxZEO9Jsjyj4fjhzOoPnWPbneuunNcdTAwMGU/ZF9cdTAwMGUyfeRV1G5cdTAwMDeH/vpcdTAwMWS7tIaUXHUwMDEx9dDk9Medkju0JVx1MDAxMchcdTAwMTWVnnVKbf3+XHUwMDA2bpmskcf79o/d5TX329Nz4c5hXHUwMDEz9Vu2gymfuN+yj8fJ9FtcdTAwMTTokHVv6cM558DR1NHZwFx1MDAxMeDoKVx1MDAwMKdYXHUwMDAxXHUwMDBmwUBdXHUwMDE5JulUMlRDq+DHnNZsKnhcdTAwMDbY+vFX8GSXeWtkioxcdTAwMDEp3lx1MDAxZiefkc9pZsp2wLG8XHUwMDBiXHUwMDE0aIK0MN5cdTAwMDNMKoaAY1x1MDAxYc+B4Vx1MDAxYbKbzH54gdtcdTAwMTJSYXu54MdQalJcdTAwMTDIpW0mQoxcIr0nN5DEU1W17fIuh2s2Pe60lEGNXHUwMDE56jFcdTAwMDSRXHUwMDExLZSWKpxcdTAwMDOybaeEwbhcXFx1MDAwMtJdXCIjnfhcdTAwMTdcIkvJtm8oKIlRkaIjPykun2jGheJ2kLTtXGbJKfL+lDV34iiGsVx0Z0Joxpn55Fx1MDAxZHIyVdu+XHUwMDEySlx1MDAxZN7uS/T38Gl3XHUwMDE2cZ79xcD24YHUXGaxZzafXc2rbVx1MDAxM9ox9slrRCvOXHUwMDE19Fx1MDAxN+9oRzImeoVcdTAwMWaCkcl14kBcdTAwMDVcdTAwMDD7uC6tKUFcdTAwMWIlU9a/mHGQ/YHtQsqZ3Vx1MDAxY9tv26Tdi4k2epZcdTAwMWJmR6cg47ZtNn6SYG2IsrUhXHUwMDEwZmBDK8JcdTAwMWRj60okXHUwMDA2o4xSKZJcdTAwMGacLGTa8ilJXFwmxlx0t9V46E2FTjFsXHUwMDE4XHUwMDE0okhUo1x1MDAwMUT+ysTnzrlnqrV9JVx1MDAxNHpIu5ZcdTAwMTUjXHSZmWy3T3lcdTAwMTmi39fz877ZbW3uXpOT/c7SycqZ9+1ged5NXHUwMDFhU8ThvTJPaqjdnFx1MDAxZDdpgCaNgCUhhGnOJ/dQbZPymHuRiJC4ffpcdTAwMTmHKVx1MDAwNUh2q/pIzGukR2o7jvOz+ZUsXHUwMDA0db+7INNX8SNcdTAwMTMwXFyYXHUwMDE0tNpZMVLsXHUwMDBi9Vx1MDAwN0T9Qr1C7MtcdTAwMWKEXHUwMDE3K+32YYCj9m7ucD682ttXXHUwMDBmb7z44LmPy0mN+NdV72Xv2oOtxYjbczT/fPnnf+6uXHUwMDA3ViJ9 + + + + ByteEditor()Container( classes="top")ByteInput()BitSwitch(0)Input( placeholder="bytes")Container()Label("0") Switch() BitSwitch(7)Label("7") Switch() ...(1 thru 6) diff --git a/src/textual/app.py b/src/textual/app.py index fa6814933..b387a718a 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -879,7 +879,7 @@ class App(Generic[ReturnType], DOMNode): char = key if len(key) == 1 else None print(f"press {key!r} (char={char!r})") key_event = events.Key(key, char) - key_event._sender = app + key_event._set_sender(app) driver.send_event(key_event) await wait_for_idle(0) diff --git a/src/textual/message.py b/src/textual/message.py index 22e877a18..7b8b3befe 100644 --- a/src/textual/message.py +++ b/src/textual/message.py @@ -84,6 +84,10 @@ class Message: """Mark this event as being forwarded.""" self._forwarded = True + def _set_sender(self, sender: MessageTarget) -> None: + """Set the sender.""" + self._sender = sender + def can_replace(self, message: "Message") -> bool: """Check if another message may supersede this one.