diff --git a/CHANGELOG.md b/CHANGELOG.md
index a00a968..30f8115 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,7 @@
## Next version
-- Add demo gif to readme
-- Fix indentation of multi-line messages
-- Stop using dataclass (for backwards compatibility with Python 3.6)
+Nothing yet
## 1.0.0 (2019-06-21)
diff --git a/README.md b/README.md
index e9b3676..696f64f 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,6 @@
A TUI client for [euphoria.io](https://euphoria.io)
-
-
## Installation
Ensure that you have at least Python 3.7 installed.
diff --git a/bowl/config.py b/bowl/config.py
index 19fb1ec..a25f599 100644
--- a/bowl/config.py
+++ b/bowl/config.py
@@ -1,3 +1,4 @@
+from dataclasses import dataclass, field
from enum import Enum, auto
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
@@ -61,17 +62,12 @@ class Kind(Enum):
Condition = Callable[[Any], bool]
+@dataclass
class Option:
- def __init__(self,
- kind: Kind,
- default: Any,
- conditions: Iterable[Tuple[Condition, str]] = frozenset(),
- ) -> None:
-
- self.kind = kind
- self.default = default
- self.conditions = conditions
+ kind: Kind
+ default: Any
+ conditions: Iterable[Tuple[Condition, str]] = field(default_factory=list)
def check_valid(self, value: Any) -> None:
if not self.kind.matches(value):
diff --git a/bowl/euphoria/euph_renderer.py b/bowl/euphoria/euph_renderer.py
index 00421c0..c999a7d 100644
--- a/bowl/euphoria/euph_renderer.py
+++ b/bowl/euphoria/euph_renderer.py
@@ -123,7 +123,7 @@ class EuphRenderer(CursorRenderer):
right = AT(self._surround_right, attributes=self._surround_attrs)
nick_str = left + nick + right + AT(" ")
- nick_spaces = AT(" " * len(nick_str))
+ nick_spaces = AT(" " * len(nick))
content = self._filter_unicode(message.content)
lines = []
diff --git a/demo.gif b/demo.gif
deleted file mode 100644
index c306d8f..0000000
Binary files a/demo.gif and /dev/null differ
diff --git a/todo.txt b/todo.txt
deleted file mode 100644
index 6f4705b..0000000
--- a/todo.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-- config
- x colors
- - key bindings
-- documentation (especially of the config)
-
-- profiling/optimisation
-
-- detail mode
-- fold threads
-- nick list
-- better key bindings/controls
-- center cursor on screen (after scrolling the view without scrolling the cursor)
-- mouse support
-- searching for messages
-- better message editing when the screen is full
-- detect when the dimensions are too small (meta width etc.) and display warning
-- green "unread message" markers
-- highlight things in messages
- - offline log browsing
- - @mentions
- - &rooms
- - https://links
- - :emojis:
- - /me s
-- word wrapping for messages
-- multi-room support
-- db backend
- - download room log
- - auto repair gaps in log
-
-x robust starting script
-x install via pip from github
- x runnable script
-x parse command-line parameters
-x nick list
-x room_widget refactor
-x save cookies
diff --git a/tree_display.py b/tree_display.py
new file mode 100644
index 0000000..2d9a4af
--- /dev/null
+++ b/tree_display.py
@@ -0,0 +1,295 @@
+# Element supply of some sort
+# Dict-/map-like
+# Tree structure
+# ↓
+# List of already formatted elements
+# Each with a line height, indentation, ...
+# List structure
+# ↓
+# Messages and UI elements rendered to lines
+# with meta-information, links/ids
+# List structure, but on lines, not individual messages
+
+class Element:
+ pass
+
+class ElementSupply:
+ pass
+
+class TreeDisplay:
+ """
+ Message line coordinates:
+
+ n - Highest message
+ ...
+ 1 - Higher message
+ 0 - Lowest message
+
+ Screen/line coordinates:
+
+ h-1 - First line
+ h-2 - Second line
+ ...
+ 1 - Second to last line
+ 0 - Last line
+
+ Terms:
+
+
+ | ...
+ | ... (above)
+ | ...
+ |
+ | | ...
+ | | | ... |
+ | ...
+ | ... (below)
+ | ...
+
+ or
+
+
+ | ...
+ | ... (above)
+ | ...
+ |
+ | ...
+ | ... (below)
+ | ...
+
+ The stem is a child of the base. The anchor is a direct or indirect child
+ of the stem, or it is the stem itself.
+
+ The base id may also be None (the implicit parent of all top-level
+ messages in a room)
+ """
+
+ def __init__(self, window: Any) -> None:
+ self.window = window
+
+ self._anchor_id = None
+ # Position of the formatted anchor's uppermost line on the screen
+ self._anchor_screen_pos = 0
+
+
+ def render(self) -> None:
+ """
+ Intermediate structures:
+ - Upwards and downwards list of elements + focused element
+ - Upwards and downwards list of rendered elements + focused element
+ - List of visible lines (saved and used for mouse clicks etc.)
+
+ Steps of rendering:
+ 1. Load all necessary elements
+ 2. Render all messages with indentation
+ 3. Compile lines
+
+ Steps of advanced rendering:
+ 1. Load focused element + render
+ 2. Load tree of focused element + render
+ 3. Load trees above and below + render, as many as necessary
+ 4. While loading and rendering trees, auto-collapse
+ 5. Move focus if focused element was hidden in an auto-collapse
+ ...?
+ """
+
+ # Step 1: Find and render the tree the anchor is in.
+
+ stem_id = self._supply.find_stem_id(self._anchor_id,
+ base=self._base_id)
+
+ tree = self._supply.get_tree(stem_id)
+ # The render might modify self._anchor_id, if the original anchor can't
+ # be displayed.
+ self._render_tree(tree)
+
+ above, anchor, below = self._split_at_anchor(tree)
+
+ # Step 2: Add more trees above and below, until the screen can be
+ # filled or there aren't any elements left in the store.
+
+ # h_win = 7
+ # 6 | <- h_above = 3
+ # 5 |
+ # 4 |
+ # 3 | <- anchor, self._anchor_screen_pos = 3, anchor.height = 2
+ # 2 |
+ # 1 | <- h_below = 2
+ # 0 |
+ #
+ # 7 - 3 - 1 = 3 -- correct
+ # 3 - 2 + 1 = 2 -- correct
+
+ height_window = None # TODO
+
+ # All values are converted to zero indexed values in the calculations
+ height_above = (height_window - 1) - self._anchor_screen_pos
+ height_below = self._anchor_screen_pos - (anchor.height - 1)
+
+ self._extend(above, height_above, base=self._base_id)
+ self._extend(below, height_below, base=self._base_id)
+
+ self._lines = self._render_to_lines(above, anchor, below)
+ self._update_window(self._lines)
+
+# TreeDisplay plan(s):
+#
+# [ ] 1. Render a basic tree thing from an ElementSupply
+# [ ] 1.1. Take/own a curses window
+# [ ] 1.2. Keep track of the text internally
+# [ ] 1.3. Retrieve elements from an ElementSupply
+# [ ] 1.4. Render elements to text depending on width of curses window
+# [ ] 1.5. Do indentation
+# [ ] 1.6. Use "..." where a thing can't be rendered
+# [ ] 2. Scroll and anchor to messages
+# [ ] 2.1. Listen to key presses
+# [ ] 2.2. Scroll up/down single lines
+# [ ] 2.3. Render starting from the anchor
+# [ ] 2.4. Some sort of culling, but preserve CONSISTENCY!
+# [ ] 3. Focus on single message
+# [ ] 3.1. Keep track of focused message
+# [ ] 3.2. Move focused message
+# [ ] 3.3. Keep message visible on screen
+# [ ] 3.4. Set anchor to focus when focus is visible
+# [ ] 3.5. Find anchor solution for when focus is offscreen
+# [ ] 4. Collapse element threads
+# [ ] 4.1. Collapse thread at any element
+# [ ] 4.2. Auto-collapse threads when they can't be displayed
+# [ ] 4.3. Focus collapsed messages
+# [ ] 4.4. Move focus when a hidden message would have focus
+# [ ] 5. Interaction with elements
+# [ ] 5.1. Forward key presses
+# [ ] 5.2. Mouse clicks + message attributes
+# [ ] 5.3. Element visibility
+# [ ] 5.4. Request more elements when the top of the element supply is hit
+# [ ] 5.5. ... and various other things
+
+# STRUCTURE
+#
+# No async!
+#
+# The TreeView "owns" and completely fills one curses window.
+#
+# When rendering things, the TreeDisplay takes elements from the ElementSupply
+# as needed. This should be a fast operation.
+#
+# When receiving key presses, the ones that are not interpreted by the TreeView
+# are passed onto the currently focused element (if any).
+#
+# When receiving mouse clicks, the clicked-on element is focused and then the
+# click and attributes of the clicked character are passed onto the focused
+# element.
+#
+# (TODO: Notify focused elements? Make "viewed/new" state for elements
+# possible?)
+#
+#
+#
+# DESIGN PRINCIPLES
+#
+# Layout: See below
+#
+# Color support: Definitely.
+# No-color-mode: Not planned.
+# => Colors required.
+
+# The tree display can display a tree-like structure of elements.
+#
+# Each element consists of:
+# 1. a way to display the element
+# 2. a way to forward key presses to the element
+# 3. element-specific attributes (Attributes), including:
+# 3.1 "id", the element's hashable id
+# 3.2 optionally "parent_id", the element's parent's hashable id
+#
+# (TODO: A way to notify the element that it is visible?)
+#
+# (TODO: Jump to unread messages, mark messages as read, up/down arrows like
+# instant, message jump tags?)
+#
+# (TODO: Curses + threading/interaction etc.?)
+#
+#
+#
+# LAYOUT
+#
+# A tree display's basic structure is something like this:
+#
+#
+# |
+# | |
+# | |
+# |
+# |
+# | |
+# | | |
+# |
+#
+# |
+#
+# It has an indentation string ("| " in the above example) that is prepended to
+# each line according to its indentation. (TODO: Possibly add different types
+# of indentation strings?)
+#
+# In general, "..." is inserted any time a message or other placeholder can't
+# be displayed. (TODO: If "..." can't be displayed, it is shortened until it
+# can be displayed?)
+#
+# Elements can be collapsed. Collapsed elements are displayed as "+ ()"
+# where is the number of elements in the hidden subtree.
+#
+# If an element is so far right that it can't be displayed, the tree display
+# first tries to collapse the tree. If the collapsed message can't be displayed
+# either, it uses "..." as mentioned above.
+#
+#
+# |
+# | |
+# | |
+# |
+# | + (3)
+# |
+#
+# |
+# | |
+# | | |
+# | | | | ...
+#
+#
+#
+# NAVIGATION
+#
+# For navigation, the following movements/keys are used (all other key presses
+# are forwarded to the currently selected element, if there is one):
+#
+# LEFT (left arrow, h): move to the selected element's parent
+#
+# RIGHT (right arrow, l): move to the selected element's first child
+#
+# UP (up arrow, k): move to the element visually below the selected element
+#
+# DOWN (down arrow, j): move to the element visually above the selected element
+#
+# Mod + LEFT: move to the selected element's previous sibling, if one exists
+#
+# Mod + RIGHT: move to the selected element's next sibling, if one exists
+#
+# Mod + UP: scroll up by scroll step
+#
+# Mod + DOWN: scroll down by scroll step
+#
+#
+# CURSES
+#
+# Main thread:
+# - async
+# - yaboli
+# - curses: non-blocking input
+# - curses: update visuals
+# - run editor in async variant of subprocess or separate thread
+#
+#
+#
+# STRUCTURE
+#
+# ???