Repos / mcross / 5e3834ce29
commit 5e3834ce29ab8333f3b7a061387ace0419c3f599
Author: Bùi Thành Nhân <hi@imnhan.com>
Date: Fri May 15 20:34:58 2020 +0700
implement list items & headings
diff --git a/README.md b/README.md
index 918ad98..166f7a2 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
It currently looks like this:
-![](https://p.caophim.net/85.png)
+![](https://p.caophim.net/87.png)
Happy-path surfing and link-visiting already works.
The UX is still terrible though (see feature checklist below).
@@ -45,7 +45,7 @@ # Feature checklist
- [x] back-forward buttons
- [ ] separate I/O thread to avoid blocking GUI
- [ ] more visual indicators - maybe a status bar at the bottom
-- [ ] parse gemini's advanced line types
+- [x] parse gemini's advanced line types
- [ ] configurable document styling
- [ ] configurable TLS to accomodate self-signed sites?
diff --git a/pyproject.toml b/pyproject.toml
index 29d7446..93f8340 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mcross"
-version = "0.3.0"
+version = "0.4.0"
description = "Do you remember www?"
authors = ["nhanb <hi@imnhan.com>"]
license = "MIT"
diff --git a/src/mcross/document.py b/src/mcross/document.py
index 37af176..d5f6864 100644
--- a/src/mcross/document.py
+++ b/src/mcross/document.py
@@ -2,6 +2,7 @@
NEWLINE = "\n"
LINK_LINE_PATTERN = re.compile(r"^=>[ \t]+(\S+)([ \t]+(.+))?$")
+HEADING_LINE_PATTERN = re.compile(r"^(#{1,3})\s+(.+)$")
class GeminiNode:
@@ -18,6 +19,22 @@ class TextNode(GeminiNode):
pass
+class ListItemNode(GeminiNode):
+ pass
+
+
+class H1Node(GeminiNode):
+ pass
+
+
+class H2Node(GeminiNode):
+ pass
+
+
+class H3Node(GeminiNode):
+ pass
+
+
class LinkNode(GeminiNode):
url: str
name: str
@@ -69,6 +86,24 @@ def parse(text):
name = match.group(3) # may be None
nodes.append(LinkNode(text=line, url=url, name=name))
+ elif line.startswith("*"):
+ nodes.append(ListItemNode(line))
+
+ elif line.startswith("#"):
+ match = HEADING_LINE_PATTERN.match(line)
+ if not match:
+ nodes.append(TextNode(line))
+ continue
+ # heading_text = match.group(2) # not used yet
+ hashes = match.group(1)
+ level = len(hashes)
+ if level == 1:
+ nodes.append(H1Node(line))
+ elif level == 2:
+ nodes.append(H2Node(line))
+ elif level == 3:
+ nodes.append(H3Node(line))
+
else:
nodes.append(TextNode(line))
diff --git a/src/mcross/gui/view.py b/src/mcross/gui/view.py
index 99ac122..c5c237d 100644
--- a/src/mcross/gui/view.py
+++ b/src/mcross/gui/view.py
@@ -1,7 +1,16 @@
import sys
from tkinter import Text, Tk, font, ttk
-from ..document import GeminiNode, LinkNode, PreformattedNode, TextNode
+from ..document import (
+ GeminiNode,
+ H1Node,
+ H2Node,
+ H3Node,
+ LinkNode,
+ ListItemNode,
+ PreformattedNode,
+ TextNode,
+)
from .model import Model
from .widgets import ReadOnlyText
@@ -102,12 +111,24 @@ def __init__(self, root: Tk, model: Model):
# prevent verticle scrollbar from disappearing when window gets small:
width=1,
)
+ text.pack(side="left", fill="both", expand=True)
text.tag_config("link", foreground="brown")
text.tag_bind("link", "<Enter>", self._on_link_enter)
text.tag_bind("link", "<Leave>", self._on_link_leave)
text.tag_bind("link", "<Button-1>", self._on_link_click)
text.tag_config("pre", font=(mono_font, 13))
- text.pack(side="left", fill="both", expand=True)
+ text.tag_config("listitem", foreground="#044604")
+
+ base_heading_font = font.Font(font=text["font"])
+ base_heading_font.config(weight="bold")
+ h1_font = font.Font(font=base_heading_font)
+ h1_font.config(size=h1_font["size"] + 8)
+ text.tag_config("h1", font=h1_font)
+ h2_font = font.Font(font=base_heading_font)
+ h2_font.config(size=h2_font["size"] + 4)
+ text.tag_config("h2", font=h2_font)
+ h3_font = font.Font(font=base_heading_font)
+ text.tag_config("h3", font=h3_font)
text_scrollbar = ttk.Scrollbar(row2, command=text.yview)
text["yscrollcommand"] = text_scrollbar.set
@@ -162,17 +183,26 @@ def render_page(self):
def render_node(node: GeminiNode, widget: Text):
nodetype = type(node)
if nodetype is TextNode:
- widget.insert("end", node.text + "\n")
+ widget.insert("end", node.text)
elif nodetype is LinkNode:
widget.insert("end", "=> ")
- widget.insert("end", f"{node.url}", ("link",))
+ widget.insert("end", node.url, ("link",))
if node.name:
widget.insert("end", f" {node.name}")
- widget.insert("end", "\n")
elif nodetype is PreformattedNode:
- widget.insert("end", f"```\n{node.text}\n```\n", ("pre",))
+ widget.insert("end", f"```\n{node.text}\n```", ("pre",))
+ elif nodetype is ListItemNode:
+ widget.insert("end", node.text, ("listitem",))
+ elif nodetype is H1Node:
+ widget.insert("end", node.text, ("h1",))
+ elif nodetype is H2Node:
+ widget.insert("end", node.text, ("h2",))
+ elif nodetype is H3Node:
+ widget.insert("end", node.text, ("h3",))
else:
- widget.insert("end", node.text + "\n")
+ widget.insert("end", node.text)
+
+ widget.insert("end", "\n")
def get_content_from_tag_click_event(event):